首页   >   代码编程

log4j使用之SocketAppender教程

之前提到了错误日志发送邮件的方式,log4j还提供了一个SocketAppender,用来将日志进行网络传输,发送到指定的服务器上,比如:大名鼎鼎的ELK就提供的有这种方式。

直接上配置文件:log4j.properties

# 配置根logger,如果下面没有自定义logger,那么项目中的日志就都是debug级别,输出到console
log4j.rootLogger=DEBUG,console,socket

# 屏蔽框架日志,只有报错的时候才放出来,优先级高于rootLogger
log4j.logger.org.springframework=ERROR
log4j.logger.org.apache.ibatis=ERROR
log4j.logger.org.mybatis.spring=ERROR
log4j.logger.com.mchange=ERROR

# 自定义包路径中的日志等级,直接写一个level即可,appender可选择,如果没写就默认使用rootLogger中配置的console
log4j.logger.com.ssm=DEBUG

# 打印出jdbc的日志
log4j.appender.java.sql.ResultSet=DEBUG
log4j.appender.java.sql.Connection=DEBUG
log4j.appender.java.sql.Statement=DEBUG
log4j.appender.java.sql.PreparedStatement=DEBUG

# 控制台(console)
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.ImmediateFlush=true
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c - %m%n

# 应用于socket
log4j.appender.socket=org.apache.log4j.net.SocketAppender
#指定日志输出级别
log4j.appender.socket.Threshold=DEBUG
#接收方地址
log4j.appender.socket.RemoteHost=localhost
#接收方端口
log4j.appender.socket.Port=5555
#本地信息(文件名、类信息、方法名、行号),默认false
log4j.appender.socket.LocationInfo=true
#延时连接
log4j.appender.socket.ReconnectionDelay=10000
#选择输出方式
log4j.appender.socket.layout=org.apache.log4j.PatternLayout
#配置日志输出格式
log4j.appender.socket.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c - %m%n

参数都很好理解,这里不做过多解释,大家直接看配置文件中的注释,唯一需要注意一下locationInfo这个参数,在下面的socket中取值会用到。

再来写一个5555端口的socket:

package com.ssm.socket;

import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by Felix on 2019/05/06.
 */
public class Log4jSocket {

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(5555);
            while (true) {
                final Socket socket = serverSocket.accept();
                new Thread() {

//                InputStream is = socket.getInputStream();
//                InputStreamReader isr = new InputStreamReader(is);
//                BufferedReader br = new BufferedReader(isr);
//                String msg;

                    // 必须要转成LoggingEvent,普通的接收方式会有问题,一堆乱码不说,还有一些看不懂的字符串
                    ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                    LoggingEvent event;
                    LocationInfo locationInfo;

                    @Override
                    public void run() {
                        try {
                            while (true) {
                                event = (LoggingEvent) ois.readObject();
                                if (event == null) {
                                    continue;
                                }
                                locationInfo = event.getLocationInformation();
                                System.out.println("文件名:" + locationInfo.getFileName());
                                System.out.println("类名:" + locationInfo.getClassName());
                                System.out.println("方法名:" + locationInfo.getMethodName());
                                System.out.println("行号:" + locationInfo.getLineNumber());
                                // 将格式拼装成log4j.properties中的格式:%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c - %m%n
                                System.out.println(sdf.format(new Date(event.getTimeStamp())) + " [" + event.getLevel() + "] - " + event.getLoggerName() + " - " + event.getMessage());
                            }
//                        while ((msg = br.readLine()) != null) {
//                            System.out.println(msg);
//                        }
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

}

关于TCP的socket,这里也不再详细叙说了,如有不明白的地方,请移步:Java Socket编写基于TCP协议的简易聊天室

先启动这个socket,再启动项目,来看一下效果:

log4j使用之SocketAppender教程

我输出了locationInfo中的值,而且还将接收到的日志组装成了和log4j.properties一样的格式。

这个socket中的代码注释,大家需要注意一下,之前在写socket的时候都是直接将IO流取出来转换成字符,这里就不行,接收log4j的日志,一定不能直接使用普通的接收方式,否则就会看到很多乱码和一堆看不懂的信息,如下图:

log4j使用之SocketAppender教程

因为log4j使用的是ObjectOutputStream,传输过来的是一个序列化的Object,大家可以看一下SocketAppender的append()方法:

public void append(LoggingEvent event) {
    if(event == null)
      return;

    if(address==null) {
      errorHandler.error("No remote host is set for SocketAppender named \""+
			this.name+"\".");
      return;
    }

    if(oos != null) {
      try {
    	 
	if(locationInfo) {
	   event.getLocationInformation();
	}
    if (application != null) {
        event.setProperty("application", application);
    }
    event.getNDC();
    event.getThreadName();
    event.getMDCCopy();
    event.getRenderedMessage();
    event.getThrowableStrRep();
    
	oos.writeObject(event);
	//LogLog.debug("=========Flushing.");
	oos.flush();
	if(++counter >= RESET_FREQUENCY) {
	  counter = 0;
	  // Failing to reset the object output stream every now and
	  // then creates a serious memory leak.
	  //System.err.println("Doing oos.reset()");
	  oos.reset();
	}
      } catch(IOException e) {
          if (e instanceof InterruptedIOException) {
              Thread.currentThread().interrupt();
          }
	      oos = null;
	      LogLog.warn("Detected problem with connection: "+e);
	      if(reconnectionDelay > 0) {
	         fireConnector();
	      } else {
	         errorHandler.error("Detected problem with connection, not reconnecting.", e,
	               ErrorCode.GENERIC_FAILURE);
	      }
      }
    }
  }

oos就是ObjectOutputStream,最终是使用oos.writeObject()方法输出了一个LoggingEvent对象,所以接收的时候,也需要使用LoggingEvent来接收。

QQ群: 686430774  /  718410762

站长Q: 1347384268

如果文章有帮到你,可以考虑请博主喝杯咖啡!

分享到:

欢迎分享本文,转载请注明出处!

作者:不忘初心

发布时间:2019-05-16

永久地址:https://www.jiweichengzhu.com/article/63fe60d93b0a401aaabe534619f4f46b

评论