搭建日志服务器
1、安装rsyslog
yum install -y rsyslog
2、修改rsyslog.conf配置文件
/etc/rsyslog.conf:
# rsyslog configuration file
# For more information see /usr/share/doc/rsyslog-*/rsyslog_conf.html
# If you experience problems, see http://www.rsyslog.com/doc/troubleshoot.html
#### MODULES ####
# The imjournal module bellow is now used as a message source instead of imuxsock.
$ModLoad imuxsock # provides support for local system logging (e.g. via logger command)
$ModLoad imjournal # provides access to the systemd journal
#$ModLoad imklog # reads kernel messages (the same are read from journald)
#$ModLoad immark # provides --MARK-- message capability
# Provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 514
# Provides TCP syslog reception
$ModLoad imtcp
$InputTCPServerRun 513
#### GLOBAL DIRECTIVES ####
template(name="DynFile" type="string" string="/var/log/system-%HOSTNAME%.log")
# Where to place auxiliary files
$WorkDirectory /var/lib/rsyslog
# Use default timestamp format
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
# File syncing capability is disabled by default. This feature is usually not required,
# not useful and an extreme performance hit
#$ActionFileEnableSync on
# Include all config files in /etc/rsyslog.d/
$IncludeConfig /etc/rsyslog.d/*.conf
# Turn off message reception via local log socket;
# local messages are retrieved through imjournal now.
$OmitLocalLogging on
# File to store the position in the journal
$IMJournalStateFile imjournal.state
#### RULES ####
# Log all kernel messages to the console.
# Logging much else clutters up the screen.
#kern.* /dev/console
# Log anything (except mail) of level info or higher.
# Don't log private authentication messages!
*.info;mail.none;authpriv.none;cron.none /var/log/messages
# The authpriv file has restricted access.
authpriv.* /var/log/secure
# Log all the mail messages in one place.
mail.* -/var/log/maillog
# Log cron stuff
cron.* /var/log/cron
# Everybody gets emergency messages
*.emerg :omusrmsg:*
# Save news errors of level crit and higher in a special file.
uucp,news.crit /var/log/spooler
# Save boot messages also to boot.log
local7.* /var/log/boot.log
# ### begin forwarding rule ###
# The statement between the begin ... end define a SINGLE forwarding
# rule. They belong together, do NOT split them. If you create multiple
# forwarding rules, duplicate the whole block!
# Remote Logging (we use TCP for reliable delivery)
#
# An on-disk queue is created for this action. If the remote host is
# down, messages are spooled to disk and sent when it is up again.
#$ActionQueueFileName fwdRule1 # unique name prefix for spool files
#$ActionQueueMaxDiskSpace 1g # 1gb space limit (use as much as possible)
#$ActionQueueSaveOnShutdown on # save messages to disk on shutdown
#$ActionQueueType LinkedList # run asynchronously
#$ActionResumeRetryCount -1 # infinite retries if host is down
# remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional
#*.* @@remote-host:514
# ### end of the forwarding rule ###
3、重启rsyslog
systemctl restart rsyslog
输入systemctl status rsyslog
,确保出现以下画面(active (running)):
[root@10-18-93-115 ccs]# systemctl status rsyslog
● rsyslog.service - System Logging Service
Loaded: loaded (/usr/lib/systemd/system/rsyslog.service; enabled; vendor preset: enabled)
Active: active (running) since 四 2022-09-08 14:19:59 CST; 1h 1min ago
Docs: man:rsyslogd(8)
http://www.rsyslog.com/doc/
Main PID: 12668 (rsyslogd)
Tasks: 9
Memory: 1.4M
CGroup: /system.slice/rsyslog.service
└─12668 /usr/sbin/rsyslogd -n
9月 08 14:19:59 10-18-93-115 systemd[1]: Starting System Logging Service...
9月 08 14:19:59 10-18-93-115 rsyslogd[12668]: [origin software="rsyslogd" swVersion="8.24.0-57.el7_9.1" x-pid="12668" x-info="http://www.rsyslog.com"] start
9月 08 14:19:59 10-18-93-115 systemd[1]: Started System Logging Service.
4、SpringBoot下配置
注:
以下是我项目中的实际使用方式
4.1、导入maven包
<!-- logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.graylog2</groupId>
<artifactId>syslog4j</artifactId>
<version>0.9.60</version>
</dependency>
4.2、新建三个java工具类
SyslogClientUtils.java:
package com.viewsources.ccs.server.syslog;
import lombok.Data;
import org.graylog2.syslog4j.Syslog;
import org.graylog2.syslog4j.SyslogConstants;
import org.graylog2.syslog4j.SyslogIF;
/**
* 转发工具类
*
* @author Yohann
* @since 2021-12-1 17:08:34
*/
@Data
public class SyslogClientUtils {
private String host;
private int port;
private String protocolType;
private static SyslogIF syslog;
private volatile static SyslogClientUtils instance;
private SyslogClientUtils(String host, int port, String protocolType) {
if ("UDP".equals(protocolType)) {
syslog = Syslog.getInstance(SyslogConstants.UDP);
} else {
syslog = Syslog.getInstance(SyslogConstants.TCP);
}
syslog.getConfig().setHost(host);
syslog.getConfig().setPort(port);
this.host = host;
this.port = port;
this.protocolType = protocolType;
}
public static SyslogClientUtils getInstance(String newHost, int newPort, String newProtocolType) {
if (instance == null || !newHost.equals(instance.getHost()) || newPort != instance.port || !newProtocolType.equals(instance.getProtocolType())) {
synchronized (SyslogClientUtils.class) {
if (instance == null || !newHost.equals(instance.getHost()) || newPort != instance.getPort()) {
instance = new SyslogClientUtils(newHost, newPort, newProtocolType);
}
}
}
return instance;
}
public void send(String info) {
syslog.log(0, info);
}
}
TcpUdpSyslogAppender.java:
package com.viewsources.ccs.server.syslog;
import ch.qos.logback.classic.net.SyslogAppender;
import ch.qos.logback.core.net.SyslogOutputStream;
import java.net.SocketException;
import java.net.UnknownHostException;
/**
* 自定义 logback syslog appender
* @author Yohann
*/
public class TcpUdpSyslogAppender extends SyslogAppender {
private String protocolType;
public String getProtocolType() {
return protocolType;
}
public void setProtocolType(String protocolType) {
this.protocolType = protocolType;
}
@Override
public SyslogOutputStream createOutputStream() throws SocketException, UnknownHostException {
return new TcpUdpSyslogOutputStream(getSyslogHost(), getPort(),protocolType);
}
}
TcpUdpSyslogOutputStream.java:
package com.viewsources.ccs.server.syslog;
import ch.qos.logback.core.net.SyslogOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.SocketException;
import java.net.UnknownHostException;
/**
* @author Yohann
*/
public class TcpUdpSyslogOutputStream extends SyslogOutputStream {
private final String ip;
private final String protocolType;
private final int port;
private final ByteArrayOutputStream baos;
public TcpUdpSyslogOutputStream(String ip, int port, String protocolType) throws UnknownHostException, SocketException {
super(ip, port);
this.ip = ip;
this.port = port;
this.protocolType = protocolType;
this.baos = new ByteArrayOutputStream();
}
private static final int MAX_LEN = 1024;
@Override
public void write(byte[] byteArray, int offset, int len) throws IOException {
baos.write(byteArray, offset, len);
}
@Override
public void flush() throws IOException {
byte[] bytes = baos.toByteArray();
// clean up for next round
baos.reset();
// after a failure, it can happen that bytes.length is zero
// in that case, there is no point in sending out an empty message/
if (bytes.length == 0) {
return;
}
SyslogClient.getInstance(ip,port,protocolType).send(new String(bytes));
}
@Override
public int getPort() {
return port;
}
@Override
public void write(int b) throws IOException {
baos.write(b);
}
}
4.3、是否开启日志转发
/**
* 更新Syslog设置
*
* @param jsonStr
*/
private void updateSysLogSetting(String jsonStr) {
ResSysLogSetting setting = JSONUtil.toBean(jsonStr, ResSysLogSetting.class);
if (setting.getStatus()) {
// 开启日志转发
if (setting.getLogType().contains(SYSTEM_LOG.getCode())) {
// 转发系统日志
startSyslogAppender("10.18.93.115", 514, "UDP");
}
} else {
// 关闭日志转发
stopSyslogAppender();
}
}
private void stopSyslogAppender() {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger root = loggerContext.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
Iterator<Appender<ILoggingEvent>> it = root.iteratorForAppenders();
Appender<ILoggingEvent> toRemoved = null;
while (it.hasNext()) {
Appender<ILoggingEvent> appender = it.next();
if (appender instanceof TcpUdpSyslogAppender) {
toRemoved = appender;
}
}
root.detachAppender(toRemoved);
}
private void startSyslogAppender(String ip, int port, String protocol) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger root = loggerContext.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
Iterator<Appender<ILoggingEvent>> it = root.iteratorForAppenders();
String newAppenderName = buildAppenderName(ip, port, protocol);
Appender<ILoggingEvent> old = null;
while (it.hasNext()) {
Appender<ILoggingEvent> appender = it.next();
if (appender instanceof TcpUdpSyslogAppender) {
old = appender;
}
}
if (old != null) {
if (old.getName().equals(newAppenderName)) {
// 新旧一致则不改动
return;
}
old.stop();
root.detachAppender(old);
}
// 添加并启动新的
TcpUdpSyslogAppender syslogAppender = new TcpUdpSyslogAppender();
syslogAppender.setSyslogHost(ip);
syslogAppender.setPort(port);
syslogAppender.setName(newAppenderName);
syslogAppender.setProtocolType(protocol);
syslogAppender.setContext(root.getLoggerContext());
syslogAppender.setFacility("LOCAL7");
syslogAppender.start();
root.addAppender(syslogAppender);
}
private String buildAppenderName(String ip, int port, String type) {
return "syslogAppender:" + ip + ":" + port + ":" + type;
}
4.4、转发mysql中的登录或操作日志
使用示例:
SyslogClientUtils udp = SyslogClientUtils.getInstance("10.18.93.115", 514, "UDP");
while (true){
udp.send("helppppppppppppppppppppppppppppp");
Thread.sleep(1000);
}
在添加操作日志或登录日志接口加上此代码:
@Override
@Async
public void addOperationLog(Caller caller, String operatorContext, String operatorObject, int operatorState) {
addOperationLog(caller.getUserId(), caller.getLoginIp(), caller.getTenantId(), caller.getLoginUa(),
operatorContext, operatorObject, operatorState, LocalDateTime.now());
// 根据系统设置是否发送操作日志到syslog
SysValue syslogSetting = sysValueService.getOne(new LambdaQueryWrapper<SysValue>().eq(SysValue::getKeyParam, SYSLOG_SETTING));
if (syslogSetting != null) {
if (StringUtils.isNotBlank(syslogSetting.getValueParam())) {
ResSysLogSetting sysLogSetting = JSONUtil.toBean(syslogSetting.getValueParam(), ResSysLogSetting.class);
if (sysLogSetting.getStatus()) {
if (sysLogSetting.getLogType().contains(OPERATION_LOG.getCode())) {
// 发送到syslog
SyslogClientUtils.getInstance("10.18.93.115", 514, "UDP").send(operatorContext);
}
}
}
}
}