扩展log4j appender发送tcp日志

版权声明:本文为博主原创文章,未经博主允许不得转载。 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

扫描二维码关注公众号,回复: 3995304 查看本文章


因此自定义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......想要去掉的话,就去直接改源码吧。。。。懒得再动了

猜你喜欢

转载自blog.csdn.net/qq_33222871/article/details/80662299