Java 正则解析logback日志

版权声明:如果觉得我的博客对你有帮助, 请点赞或评论! https://blog.csdn.net/zongf0504/article/details/89001958

对于web应用, 笔者通常会在请求开始和结束的时候打印一行日志, 并记录接口的处理时间, 也就是说通常所说的接口响应时间. 这样当生产环境项目出现异常时, 可以通过接口的响应时间和次数初步推测有可能产生问题的接口, 从而更快定位和解决问题。

1. 日志格式

笔者习惯于使用logback 输出日志, logback 定义日志格式比较灵活, 笔者定义的格式为:

  • 接口开始: t i m e [ time[ level][ t h r e a d N a m e ] [ threadName][ className][ s e s s i o n I d ] [ sessionId][ requestId}-接口:[$url]-start
  • 接口结束: t i m e [ time[ level][ t h r e a d N a m e ] [ threadName][ className][ s e s s i o n I d ] [ sessionId][ requestId}-接口:[$url]-ended, 耗时:254ms
2019-04-01 13:38:20.591[INFO][http-127.0.0.1-8080-1][org.zongf.test.ApiAccessLogUtil][8BD0EF4739EB58C634D1853B2F7BAF96][67617bd63bb4437295eb6a49601d4b0f]-接口:[/article/1001]-start
2019-04-01 13:38:20.591[INFO][http-127.0.0.1-8080-1][org.zongf.test.ApiAccessLogUtil][8BD0EF4739EB58C634D1853B2F7BAF96][67617bd63bb4437295eb6a49601d4b0f]-接口:[/article/1001]-这是其它日志, 模拟不匹配
2019-04-01 13:38:22.367[INFO][http-127.0.0.1-8080-1][org.zongf.test.ApiAccessLogUtil][8BD0EF4739EB58C634D1853B2F7BAF96][67617bd63bb4437295eb6a49601d4b0f]-接口:[/article/1001]-ended, 访问时间:254ms

2. 解析日志

2.1 建立日志模型

将单行日志抽象为java 类, 这样解析之后便于后续做排序, 统计等操作。

public class LogLine {

    // 日志输出时间
    private String time;

    // 日志级别
    private String level;

    // 日志线程名称
    private String thread;

    // 日志类名称
    private String clz;

    // 请求会话 id
    private String sid;

    // 请求id
    private String reqid;

    // 接口地址
    private String uri;

    // 日志类型: start-开始日志 ended,-结束日志
    private String tag;

    // 请求耗时
    private int cost;
	
	// 省略setter/getter 方法
}

2.2 日志解析工具类

笔者编写一个解析单行日志的工具类, 用于将单行日志转换为java 模型.

public class LogLineParserUtil {

    /* 正则表达式模式说明:
       匹配时间: ([^\[\]]*)
       匹配日志级别,线程名称等被中括号包裹的字符串: \[([^\[\]]*)\]
       精确匹配字符串start或ended: (start|ended,)
       匹配剩余字符:(.*)
     */

    // 日志匹配模式表达式. 用括号表示捕获匹配内容, 在后面可以时候用matcher.group(idx)获取捕获的字符串内容
    private static final String pattenExp = "([^\\[\\]]*)\\[([^\\[\\]]*)\\]\\[([^\\[\\]]*)\\]\\[([^\\[\\]]*)\\]\\[([^\\[\\]]*)\\]\\[([^\\[\\]]*)\\]-接口:\\[([^\\[\\]]*)\\]\\-(start|ended,)(.*)";

    private static Pattern pattern ;

    static {
        pattern = Pattern.compile(pattenExp);
    }

    /**
     * @Description: 解析单行日志
     * @param line 日志
     * @return: LogLine
     * @author: zongf
     * @time: 2019-04-02 11:08:37
     */
    public static LogLine parserLine(String line) {

        // 匹配单行日志
        Matcher matcher = pattern.matcher(line);
        boolean isFind = matcher.find();

        if (isFind) {
            // 匹配成功, 则创建日志模型
            LogLine logLine = new LogLine();

            // 赋值每个字段
            logLine.setTime(matcher.group(1));
            logLine.setLevel(matcher.group(2));
            logLine.setThread(matcher.group(3));
            logLine.setClz(matcher.group(4));
            logLine.setSid(matcher.group(5));
            logLine.setReqid(matcher.group(6));
            logLine.setUri(matcher.group(7));
            logLine.setTag(matcher.group(8));

            // 如果单行日志类型为ended, 则处理耗时字段
            if ("ended,".equals(logLine.getTag())) {
                String cost = StringUtils.substringBetween(matcher.group(9),":", "ms");
                logLine.setCost(Integer.parseInt(cost));
            }
            return logLine;
        }

        //匹配失败, 返回空
        return null;
    }
}

2.3 测试用例

笔者这里只测试将单行日志解析为java 对象, 对于读取文件, 日志统计排序就不介绍了, 都是java 的基本功.

    @Test
    public void test_parseLog(){
        String[] logs = new String[]{
                "2019-04-01 13:38:20.591[INFO][http-127.0.0.1-8080-1][org.zongf.test.ApiAccessLogUtil][8BD0EF4739EB58C634D1853B2F7BAF96][67617bd63bb4437295eb6a49601d4b0f]-接口:[/article/1001]-start",
                "2019-04-01 13:38:20.591[INFO][http-127.0.0.1-8080-1][org.zongf.test.ApiAccessLogUtil][8BD0EF4739EB58C634D1853B2F7BAF96][67617bd63bb4437295eb6a49601d4b0f]-接口:[/article/1001]-这是其它日志, 模拟不匹配",
                "2019-04-01 13:38:22.367[INFO][http-127.0.0.1-8080-1][org.zongf.test.ApiAccessLogUtil][8BD0EF4739EB58C634D1853B2F7BAF96][67617bd63bb4437295eb6a49601d4b0f]-接口:[/article/1001]-ended, 访问时间:254ms"
        };

        for (String log : logs) {
            LogLine logLine = LogLineParserUtil.parserLine(log);
            System.out.println(logLine);
        }
    }

2.4 解析结果

笔者自定义toString方法, 只输出几个字段用于测试。 从输出结果来看, 由于第二行日志和笔者自定义的正则匹配模式不匹配, 所以并未做正确解析.

{time='2019-04-01 13:38:20.591', cost=0, uri='/article/1001', sid='8BD0EF4739EB58C634D1853B2F7BAF96', reqid='67617bd63bb4437295eb6a49601d4b0f'}
null
{time='2019-04-01 13:38:22.367', cost=254, uri='/article/1001', sid='8BD0EF4739EB58C634D1853B2F7BAF96', reqid='67617bd63bb4437295eb6a49601d4b0f'}

猜你喜欢

转载自blog.csdn.net/zongf0504/article/details/89001958
今日推荐