log4j学习 异常日志的捕获 使用线程池或者rabbitmq实现日志的异步存储

最近项目中使用到了ES用来记录日志,用起来非常的方便,基本的记录如下,实现了类似下面的效果。

这个架构不是本人搭建,自己也顺便学了一下。

下面先简单介绍一下使用log4j将日志存储在数据库,不管存储在哪,原理都是一样,只要明白日志的工作机制,就很容易搞懂,实现起来很简单。整体一个思路如下:

1.log4j.properties文件的写入

当然在这个文件里可以直接配置写入日志到MySQL,但是我没有这么做。

# Root logger option
log4j.rootLogger=WARN, stdout, file, db

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File = logs/log.log
log4j.appender.file.Append = true
log4j.appender.file.Threshold = ERROR
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} %5p %c{1}:%L - %m%n

log4j.appender.db =com.gysoft.demo.DBAppender

需要说明一下,最后一行是处理日志基本信息的类。

2.处理日志信息的核心类

package com.gysoft.demo;

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

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Enumeration;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @Description
 * @Author DJZ-WWS
 * @Date 2019/5/6 14:22
 */
public class DBAppender extends AppenderSkeleton {


    LogService logService = new LogService();

    Log log = new Log();

    @Override
    protected void append(LoggingEvent loggingEvent) {
        ThrowableInformation throwableInformation = loggingEvent.getThrowableInformation();
        if (throwableInformation != null) {
            LocationInfo locationInfo = loggingEvent.getLocationInformation();
            Throwable throwable = throwableInformation.getThrowable();
            System.out.println(loggingEvent.toString());
            //记录日志的基本信息

            try {
                log.setClassName(loggingEvent.getLocationInformation().getClassName());
                log.setFileName(loggingEvent.getLocationInformation().getFileName());
                log.setLineNumber(loggingEvent.getLocationInformation().getLineNumber());
                log.setMethodName(loggingEvent.getLocationInformation().getMethodName());
                log.setServerIp(getIp());
                log.setLogName(loggingEvent.getLoggerName());
                log.setLogLevel(loggingEvent.getLogger().getLevel().toString());
                log.setLogThread(loggingEvent.getThreadName());
                log.setLogMills(new Date(loggingEvent.getTimeStamp()));
                log.setLogMessage(loggingEvent.getMessage().toString());
                log.setThrowMessage(throwable.getMessage());
                log.setThrowDetailMessage(throwable.toString());
                log.setThrowStackTrace(throwable.getStackTrace().toString());
            } catch (Exception e) {
                errorHandler.error("日志信息封装异常");
            }
            System.out.println(log.toString());
            //logService.saveLog(log);
            saveLog(log);
            System.out.println("日志保存已经交给了线程池处理,主线程继续执行后面的业务,做到异步的效果");
        }
    }


    private   void  saveLog(final Log log){

        //使用线程保存日志 使用异步的方式实现日志的存储
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
        executorService.schedule(new Runnable() {
            public void run() {
                try {
                    //增加一个延时,看到异步的效果
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            //日志的存储
                logService.saveLog(log);
            }
        },5, TimeUnit.MILLISECONDS);
    }

    public void close() {

    }

    public boolean requiresLayout() {
        return false;
    }

    private String getIp() throws UnknownHostException {
        try {
            InetAddress candidateAddress = null;
            // 遍历所有的网络接口
            for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {
                NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
                // 在所有的接口下再遍历IP
                for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {
                    InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();
                    if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址
                        if (inetAddr.isSiteLocalAddress()) {
                            // 如果是site-local地址,就是它了
                            return inetAddr.getHostAddress();
                        } else if (candidateAddress == null) {
                            // site-local类型的地址未被发现,先记录候选地址
                            candidateAddress = inetAddr;
                        }
                    }
                }
            }
            if (candidateAddress != null) {
                return candidateAddress.getHostAddress();
            }
            // 如果没有发现 non-loopback地址.只能用最次选的方案
            InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
            if (jdkSuppliedAddress == null) {
                throw new UnknownHostException("The JDK InetAddress.getLocalHost() method unexpectedly returned null.");
            }
            return jdkSuppliedAddress.getHostAddress();
        } catch (Exception e) {
            UnknownHostException unknownHostException = new UnknownHostException(
                    "Failed to determine LAN address: " + e);
            unknownHostException.initCause(e);
            throw unknownHostException;
        }
    }
}

3.  保存日志的业务层

这里没有引入MySQL,如果自己需要实现,请添加自己的数据库连接,保存logbean对象即可。

package com.gysoft.demo;

/**
 * @Description
 * @Author DJZ-WWS
 * @Date 2019/5/6 14:28
 */
public class LogService {


    /**
        假装这里有MySQL
     * 保存日志
     */
    public   void  saveLog(Log log){
        System.out.println("插入日志bean,保存日志到数据库");
    }
}

4.封装日志基本信息的bean类

package com.gysoft.demo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @Description  记录的信息
 * @Author DJZ-WWS
 * @Date 2019/5/6 14:17
 */

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Log {


  private String className;

  private  String  fileName;

  private  String lineNumber;

  private   String  methodName;
  private   String serverIp;
  private  String  logName;
  private   String  logLevel;
  private   String  logThread;
   /**
    * 日志时间
    */
  private Date  logMills;

  private String  logMessage;
  private  String  throwMessage;
  private  String   throwDetailMessage;
  private  String  throwStackTrace;
}

5.日志的测试类

package com.gysoft.demo;


import org.apache.log4j.Logger;

/**
 * @Description
 * @Author DJZ-WWS
 * @Date 2019/5/6 10:09
 */
public class DemoTest {


    private static Logger logger = (Logger) Logger.getLogger(DemoTest.class);


    public static void main(String[] args) {

             //PropertyConfigurator.configure("log4j.properties");

        int  a=1;

        try {
            if(a==1){
                throw new  IllegalStateException("异常异常了");
            }
        } catch (Exception e) {
            logger.error("测试异常链的捕获 error", e);

        }

        logger.error("Demoe Test  ----error");

        logger.info("DemoTest----info");

        logger.info("DemoTest----debugger");
        logger.warn("Demotest -----warn");
        logger.trace("DemoTest----trace");
        logger.fatal("DemoTest -----fatal");
    
    }
}

6.日志打印结果

16:39:21,917 ERROR DemoTest:28 - 测试异常链的捕获 error
java.lang.IllegalStateException: 异常异常了
	at com.gysoft.demo.DemoTest.main(DemoTest.java:25)
org.apache.log4j.spi.LoggingEvent@1d56ce6a
log4j:ERROR 日志信息封装异常
Log(className=com.gysoft.demo.DemoTest, fileName=DemoTest.java, lineNumber=28, methodName=main, serverIp=192.168.3.227, logName=com.gysoft.demo.DemoTest, logLevel=null, logThread=null, logMills=null, logMessage=null, throwMessage=null, throwDetailMessage=null, throwStackTrace=null)
插入日志bean,保存日志到数据库
16:39:21,986 ERROR DemoTest:32 - Demoe Test  ----error
16:39:21,986  WARN DemoTest:37 - Demotest -----warn
16:39:21,986 FATAL DemoTest:39 - DemoTest -----fatal

7.补充pom依赖


<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.18</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.46</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>

</dependencies>

总结:其实很简单,就是日志信息的总结起来就三个步骤  捕获,封装,存储。对于存储的位置就需要我们自己选择了,推荐使用Es。如果需要存储到es将对象进行json处理即可,存储到es,再通过es查询,就可以达到最开始我们需要的那种结果。

当然你如果接的log4j不好用的话你也可以使用logback,或者log4j2原理都是一样的。

这里还没有更新整合spring,rabbitmq保存日志相关东西,后续会慢慢更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值