1.SyslogAppender
logback 有 SyslogAppender,发送 syslog 日志,但是只支持发送 udp syslog 日志。需自定义 Appender,发送 tcp syslog 日志。
2.自定义 Appender
pom 文件
<!-- slf4j依赖包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<!-- logback-classic桥接器 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
<!-- logback实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.11</version>
</dependency>
public class CustomSyslogAppender extends SyslogAppender {
@Override
public SyslogOutputStream createOutputStream() throws SocketException, UnknownHostException {
return new CustomSyslogOutputStream(getSyslogHost(), getPort());
}
public static class CustomSyslogOutputStream extends SyslogOutputStream {
private static final int MAX_LEN = 1024;
private final String syslogHost;
private final int port;
private Socket socket;
private OutputStream outputStream;
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
public CustomSyslogOutputStream(String syslogHost, int port) throws UnknownHostException, SocketException {
super(syslogHost, port);
this.syslogHost = syslogHost;
this.port = port;
// 建立 TCP 连接(延迟到第一次 flush 时再连接,避免初始化失败阻塞)
}
@Override
public void write(int b) throws IOException {
baos.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
baos.write(b, off, len);
}
@Override
public void flush() throws IOException {
if (baos.size() == 0) {
return;
}
byte[] bytes = baos.toByteArray();
if (bytes.length == 0) {
return;
}
// 确保连接
ensureConnected();
try {
outputStream.write(bytes);
// 可选:自动添加换行符(很多 Syslog 服务器依赖换行符分隔日志)
outputStream.write('\n');
outputStream.flush();
} catch (IOException e) {
// 连接出错,关闭 socket,下次重连
closeQuietly();
throw e;
}
// 重置缓冲区
if (baos.size() > MAX_LEN) {
baos = new ByteArrayOutputStream();
} else {
baos.reset();
}
}
@Override
public void close() {
closeQuietly();
}
/**
* 确保连接已建立
*/
private synchronized void ensureConnected() throws IOException {
if (socket == null || socket.isClosed() || !socket.isConnected()) {
socket = new Socket(syslogHost, port);
outputStream = socket.getOutputStream();
}
}
private void closeQuietly() {
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException ignored) {
}
try {
if (socket != null && !socket.isClosed()) {
socket.close();
}
} catch (IOException ignored) {
}
socket = null;
outputStream = null;
}
}
}
logback.xml 文件
<configuration>
<!-- 控制台 Appender -->
<appender name="CONSOLE_1" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--
<appender name="SYSLOG_APPENDER_1" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>514</port>
<facility>LOCAL0</facility>
</appender>
-->
<!-- 定义一个 SocketAppender -->
<appender name="CUSTOM_SYSLOG_APPENDER_1" class="org.example.CustomSyslogAppender">
<syslogHost>localhost</syslogHost>
<port>514</port>
<facility>LOCAL0</facility>
</appender>
<!-- 根日志器使用 CONSOLE Appender -->
<root level="INFO">
<appender-ref ref="CONSOLE_1" />
<!--<appender-ref ref="SYSLOG_APPENDER_1" />-->
<appender-ref ref="CUSTOM_SYSLOG_APPENDER_1" />
</root>
</configuration>
启动类:
public class Main {
public static void main(String[] args) throws InterruptedException {
org.slf4j.Logger logger = LoggerFactory.getLogger(Main.class);
logger.info("Hello 1");
}
}
3.结果
可以看到,日志写入到服务的 /var/log/syslog 文件
zxm@zxm-pc:~$ tail -n 1 /var/log/syslog
Oct 1 23:21:28 zxm-pc [main] org.example.Main Hello 1