版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33222871/article/details/80662299
之前使用logstash采集log4j日志,使用logstash-tcp-input插件,log4j使用SocketAppender;
发现log4j的socket竟然不支持layout,所以logstash收到的消息就只有%m里的东西,,,什么时间啊,线程啊这些统统收不到;
所以,我要重写appender来发送socket消息
log4j自带的socketAppender是org.apache.log4j.net.SocketAppender,
继承关系如下
java.lang.Object
org.apache.log4j.AppenderSkeleon
org.apache.log4j.net.SocketAppender
因此自定义appender继承自AppenderSkeleon(原本打算继承SocketAppender,研究后发现,这样没办法实现我的需求)
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 用于logstash的socket模块,,解决各种问题
*
* @author 墨盒
*/
public class LogstashSocketAppender extends AppenderSkeleton {
private Layout layout;
private String host;
private OutputStreamWriter out;
private int port = 4560;
private String ip;
private boolean syn = false;//同步或异步
private boolean requireIp = true;//是否需要输出ip
public LogstashSocketAppender() {
}
public void setSyn(boolean syn) {
this.syn = syn;
}
public void setRequireIp(boolean requireIp) {
this.requireIp = requireIp;
}
private void getIp() {
if (requireIp) {
try {
ip = InetAddress.getLocalHost().getHostAddress() + "-";
} catch (UnknownHostException e) {
e.printStackTrace();
}
} else {
ip = "";
}
}
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
private void connect() {
try {
if (syn) {
new Thread(() -> {
try {
createOut();
} catch (IOException e) {
e.printStackTrace();
LogLog.warn("can't connect the " + this.host);
}
}).start();
} else {
createOut();
}
} catch (Exception e) {
LogLog.error("can't connect the " + this.host);
}
}
private void createOut() throws IOException {
out = new OutputStreamWriter(new Socket(this.host, this.port).getOutputStream());
}
@Override
public void activateOptions() {
connect();
getIp();
}
@Override
protected void append(LoggingEvent loggingEvent) {
if (this.out != null && this.layout != null) {
String message = this.layout.format(loggingEvent);
try {
if (syn && out != null) {//完成连接前的日志都将丢失
new Thread(() -> {
try {
out.write(System.currentTimeMillis() + "-" + ip + message);
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
} else {
this.out.write(System.currentTimeMillis() + "-" + ip + message);
this.out.flush();
}
} catch (IOException e) {
if (e instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
this.out = null;
LogLog.error("send socket message fail");
}
}
}
@Override
public void close() {
if (this.out != null) {
try {
this.out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void setLayout(Layout layout) {
this.layout = layout;
}
@Override
public boolean requiresLayout() {
return true;
}
}
改appender支持异步发送消息,注意,如果logger初始化的时候,日志接收端未启动,不影响程序运行,在socket超时时间内启动接收端即可收到日志,但是在此之前的日志将会丢失
log4j配置使用方法如下
log4j.appender.logstash=cn.inkroom.web.frame.log.LogstashSocketAppender
# socket接收端ip
log4j.appender.logstash.host=127.0.0.1
# socket接收端端口,默认4560
log4j.appender.logstash.port=4566
# 是否采用异步输出,默认false
log4j.appender.logstash.syn=true
# 是否需要在日志消息前加入ip,默认为false
log4j.appender.logstash.requireIp=true
# 消息模板
log4j.appender.logstash.layout=org.apache.log4j.PatternLayout
log4j.appender.logstash.layout.ConversionPattern=(四期) %d %p [%c] - <%m>%n
注:暂不支持socket断线重连
再注:由于我写这个是为了做日志采集,因此每条消息前会自动带入一个时间戳,em......想要去掉的话,就去直接改源码吧。。。。懒得再动了