文档章节

logback是怎么知道java代码的行数的?

强子哥哥
 强子哥哥
发布于 2017/12/07 18:26
字数 1183
阅读 2248
收藏 95

使用logback,忽然想到这个问题,然后问了几个同事都没研究过,我来看看logback是如何知道的

打断点如下:

stop in com.sql.mysql.sharding.plugin.ExecutorInterceptor.intercept

stop in ch.qos.logback.classic.Logger.callAppenders

stop in ch.qos.logback.core.encoder.LayoutWrappingEncoder.encode

encode的java函数如下:

    public byte[] encode(E event) {
        String txt = layout.doLayout(event);
        return convertToBytes(txt);
    }

那么layout的值是啥呢?

main[1] print layout
 layout = "ch.qos.logback.classic.PatternLayout("%level %date , %logger{36} [%file:%line] , Thread-[%thread] - %msg%n")"

就是我们自己定义的格式,好,继续往下走。

碰到了这个函数

​
    protected String writeLoopOnConverters(E event) {
        StringBuilder strBuilder = new StringBuilder(INTIAL_STRING_BUILDER_SIZE);
        Converter<E> c = head;
        while (c != null) {
            c.write(strBuilder, event);
            c = c.getNext();
        }
        return strBuilder.toString();
    }
​

这里看来是做了一个字符串拼接的功能!,继续

========================================================

Step completed: "thread=main", ch.qos.logback.core.pattern.PatternLayoutBase.writeLoopOnConverters(), line=113 bci=11
113            Converter<E> c = head;

main[1] print head
 head = "ch.qos.logback.classic.pattern.LevelConverter@cf3a8fb"

其实也就可以猜到,这里是要一个链表的converter,每个converter构造自己的内容

我们来看看logback提供了哪些converter

---

未完待续,好,继续

1)ch.qos.logback.classic.pattern.LevelConverter

@Override
    final public void write(StringBuilder buf, E event) {
        String s = convert(event);

        if (formattingInfo == null) {
            buf.append(s);
            return;
        }

        int min = formattingInfo.getMin();
        int max = formattingInfo.getMax();

        if (s == null) {
            if (0 < min)
                SpacePadder.spacePad(buf, min);
            return;
        }

        int len = s.length();

        if (len > max) {
            if (formattingInfo.isLeftTruncate()) {
                buf.append(s.substring(len - max));
            } else {
                buf.append(s.substring(0, max));
            }
        } else if (len < min) {
            if (formattingInfo.isLeftPad()) {
                SpacePadder.leftPad(buf, s, min);
            } else {
                SpacePadder.rightPad(buf, s, min);
            }
        } else {
            buf.append(s);
        }
    }

2)ch.qos.logback.classic.pattern.DateConverter

 3)ch.qos.logback.classic.pattern.LoggerConverter

public class LoggerConverter extends NamedConverter {

    protected String getFullyQualifiedName(ILoggingEvent event) {
        return event.getLoggerName();
    }
}

这个在event创建时就赋值了,这样就知道是哪个类里面的log打的消息了

4)ch.qos.logback.classic.pattern.FileOfCallerConverter

获取Java文件

    public StackTraceElement[] getCallerData() {
        if (callerDataArray == null) {
            callerDataArray = CallerData
                            .extract(new Throwable(), fqnOfLoggerClass, loggerContext.getMaxCallerDataDepth(), loggerContext.getFrameworkPackages());
        }
        return callerDataArray;
    }

看起来是获得了一个栈,具体是什么呢,进去看

public static StackTraceElement[] extract(Throwable t, String fqnOfInvokingClass, final int maxDepth, List<String> frameworkPackageList) {
        if (t == null) {
            return null;
        }

        StackTraceElement[] steArray = t.getStackTrace();
        StackTraceElement[] callerDataArray;

        int found = LINE_NA;
        for (int i = 0; i < steArray.length; i++) {
            if (isInFrameworkSpace(steArray[i].getClassName(), fqnOfInvokingClass, frameworkPackageList)) {
                // the caller is assumed to be the next stack frame, hence the +1.
                found = i + 1;
            } else {
                if (found != LINE_NA) {
                    break;
                }
            }
        }

        // we failed to extract caller data
        if (found == LINE_NA) {
            return EMPTY_CALLER_DATA_ARRAY;
        }

        int availableDepth = steArray.length - found;
        int desiredDepth = maxDepth < (availableDepth) ? maxDepth : availableDepth;

        callerDataArray = new StackTraceElement[desiredDepth];
        for (int i = 0; i < desiredDepth; i++) {
            callerDataArray[i] = steArray[found + i];
        }
        return callerDataArray;
    }

其实就是利用了一个new Throwable()的t.getStackTrace(); 来获取一个栈,

但是其实有很多个,那么这么识别出来我要到哪一层呢?

[1] ch.qos.logback.classic.spi.CallerData.extract (CallerData.java:63)
  [2] ch.qos.logback.classic.spi.LoggingEvent.getCallerData (LoggingEvent.java:258)
  [3] ch.qos.logback.classic.pattern.FileOfCallerConverter.convert (FileOfCallerConverter.java:22)
  [4] ch.qos.logback.classic.pattern.FileOfCallerConverter.convert (FileOfCallerConverter.java:19)
  [5] ch.qos.logback.core.pattern.FormattingConverter.write (FormattingConverter.java:36)
  [6] ch.qos.logback.core.pattern.PatternLayoutBase.writeLoopOnConverters (PatternLayoutBase.java:115)
  [7] ch.qos.logback.classic.PatternLayout.doLayout (PatternLayout.java:141)
  [8] ch.qos.logback.classic.PatternLayout.doLayout (PatternLayout.java:39)
  [9] ch.qos.logback.core.encoder.LayoutWrappingEncoder.encode (LayoutWrappingEncoder.java:115)
  [10] ch.qos.logback.core.OutputStreamAppender.subAppend (OutputStreamAppender.java:230)
  [11] ch.qos.logback.core.OutputStreamAppender.append (OutputStreamAppender.java:102)
  [12] ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend (UnsynchronizedAppenderBase.java:84)
  [13] ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders (AppenderAttachableImpl.java:51)
  [14] ch.qos.logback.classic.Logger.appendLoopOnAppenders (Logger.java:270)
  [15] ch.qos.logback.classic.Logger.callAppenders (Logger.java:257)
  [16] ch.qos.logback.classic.Logger.buildLoggingEventAndAppend (Logger.java:421)
  [17] ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus (Logger.java:383)
  [18] ch.qos.logback.classic.Logger.info (Logger.java:579)
  [19] com.sql.mysql.sharding.plugin.ExecutorInterceptor.intercept (ExecutorInterceptor.java:53)
  [20] org.apache.ibatis.plugin.Plugin.invoke (Plugin.java:61)
  [21] com.sun.proxy.$Proxy3.update (null)
  [22] org.apache.ibatis.session.defaults.DefaultSqlSession.update (DefaultSqlSession.java:198)
  [23] org.apache.ibatis.session.defaults.DefaultSqlSession.delete (DefaultSqlSession.java:213)
  [24] org.apache.ibatis.binding.MapperMethod.execute (MapperMethod.java:67)
  [25] org.apache.ibatis.binding.MapperProxy.invoke (MapperProxy.java:59)
  [26] com.sun.proxy.$Proxy4.deleteRole (null)
  [27] notransaction.ShardingNoTransaction.main (ShardingNoTransaction.java:24)

其实有一行代码

for (int i = 0; i < steArray.length; i++) {
            if (isInFrameworkSpace(steArray[i].getClassName(), fqnOfInvokingClass, frameworkPackageList)) {
                // the caller is assumed to be the next stack frame, hence the +1.
                found = i + 1;
            } else {
                if (found != LINE_NA) {
                    break;
                }
            }
        }

关键就是这行代码isInFrameworkSpace

static boolean isInFrameworkSpace(String currentClass, String fqnOfInvokingClass, List<String> frameworkPackageList) {
        // the check for org.apache.log4j.Category class is intended to support
        // log4j-over-slf4j. it solves http://bugzilla.slf4j.org/show_bug.cgi?id=66
        if (currentClass.equals(fqnOfInvokingClass) || currentClass.equals(LOG4J_CATEGORY) || currentClass.startsWith(SLF4J_BOUNDARY)
                        || isInFrameworkSpaceList(currentClass, frameworkPackageList)) {
            return true;
        } else {
            return false;
        }
    }

如果是

1)"ch.qos.logback.classic.Logger"

2)"org.apache.log4j.Category"

3)以"org.slf4j.Logger";开头

4)或者以frameworkPackageList里面的值开头的都是日志框架的

直到找到第一个我们业务自己的为止!

然后把调用栈拷贝出来,再执行下面的函数

    public String convert(ILoggingEvent le) {
        StackTraceElement[] cda = le.getCallerData();
        if (cda != null && cda.length > 0) {
            return cda[0].getFileName();
        } else {
            return CallerData.NA;
        }
    }
Step completed: "thread=main", ch.qos.logback.core.pattern.FormattingConverter.write(), line=36 bci=5
36            String s = convert(event);

main[1] step
> 
Step completed: "thread=main", ch.qos.logback.core.pattern.FormattingConverter.write(), line=38 bci=6
38            if (formattingInfo == null) {

main[1] print s
 s = "ExecutorInterceptor.java"

这样就获得了文件名

5)ch.qos.logback.classic.pattern.LineOfCallerConverter

这里会复用上一个步骤的结果

Step completed: "thread=main", ch.qos.logback.classic.spi.LoggingEvent.getCallerData(), line=257 bci=0
257            if (callerDataArray == null) {

main[1] print callerDataArray
 callerDataArray = instance of java.lang.StackTraceElement[8] (id=2233)
/**
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *
 *   or (per the licensee's choosing)
 *
 * under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation.
 */
package ch.qos.logback.classic.pattern;

import ch.qos.logback.classic.spi.CallerData;
import ch.qos.logback.classic.spi.ILoggingEvent;

public class LineOfCallerConverter extends ClassicConverter {

    public String convert(ILoggingEvent le) {
        StackTraceElement[] cda = le.getCallerData();
        if (cda != null && cda.length > 0) {
            return Integer.toString(cda[0].getLineNumber());
        } else {
            return CallerData.NA;
        }
    }

}

这样就获得了行号!!!

6) ch.qos.logback.classic.pattern.ThreadConverter

/**
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *
 *   or (per the licensee's choosing)
 *
 * under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation.
 */
package ch.qos.logback.classic.pattern;

import ch.qos.logback.classic.spi.ILoggingEvent;

/**
 * Return the events thread (usually the current thread).
 * 
 * @author Ceki G&uuml;lc&uuml;
 */
public class ThreadConverter extends ClassicConverter {

    public String convert(ILoggingEvent event) {
        return event.getThreadName();
    }

}

线程名

    public String getThreadName() {
        if (threadName == null) {
            threadName = (Thread.currentThread()).getName();
        }
        return threadName;
    }

然后还有其它的一些颜色配置,这个看

http://blog.csdn.net/java_zone/article/details/54341029

就好了,需要在真实的shell上才生效,eclipse里的控制台不生效。

好,完毕!

© 著作权归作者所有

共有 人打赏支持
强子哥哥

强子哥哥

粉丝 859
博文 900
码字总数 615641
作品 8
南京
架构师
加载中

评论(17)

强子哥哥
强子哥哥

引用来自“zzuqiang”的评论

深入讲解很赞
谢谢!好奇,顺便就 debug下
海力布
海力布

引用来自“许雷神”的评论

楼主,可知logback不能按日期来切分日志。我配置了logback,上线一段时间后,它所有的日志都挤在一起,log变得庞大的很。不知道怎么破

@许雷神 我知道,前几天刚遇到这个问题
zzuqiang
zzuqiang
深入讲解很赞
强子哥哥
强子哥哥

引用来自“巴林的狗尾草”的评论

我去,首先,行数哪儿来的,行数是编译的时候编译到字节码的,然后怎么找到调用日志的地方,通过上下文的stacktrace来弄的
你想表达的是?
巴林的狗尾草
巴林的狗尾草
我去,首先,行数哪儿来的,行数是编译的时候编译到字节码的,然后怎么找到调用日志的地方,通过上下文的stacktrace来弄的
强子哥哥
强子哥哥

引用来自“lxbzmy”的评论

知道代码行数的只有唯一一个路子就是getTrace
:laughing:
lxbzmy
lxbzmy
知道代码行数的只有唯一一个路子就是getTrace
Feng_Yu
Feng_Yu

引用来自“许雷神”的评论

楼主,可知logback不能按日期来切分日志。我配置了logback,上线一段时间后,它所有的日志都挤在一起,log变得庞大的很。不知道怎么破
官方文档就有这个范例,为何不去看?
强子哥哥
强子哥哥

引用来自“Rober萝卜”的评论

一直很好奇这个是怎么实现的,谢谢分享!
我也是好奇就顺便debug了下
强子哥哥
强子哥哥

引用来自“AllenBlock”的评论

isInFrameworkSpace
这个if...else...
也是if(true){ return true;}else{return false}
你看的不是子类的具体实现吧
如何开启Dubbo框架内部的日志?

欢迎加入DUBBO交流群:259566260 这里将对如何在自己的项目里面开启dubbo框架自己的日志,并对输出的日志进行控制。在讲这些之前,先看看dubbo在处理日志的时候是怎么做的? 在dubbo框架内所...

Bieber
2015/03/25
0
0
log4j on sentry实践

1、sentry 2、log4j/logback on sentry 代码中集成Sentry 使用Maven: io.sentry sentry-logback 1.7.5 使用SBT: libraryDependencies += "io.sentry" % "sentry-logback" % "1.7.5" lo......

巧克力黒
07/25
0
0
Java日志,需要知道的几件事(commons-logging,log4j,slf4j,logback)

如果对于commons-loging、log4j、slf4j、LogBack等都已经非常清楚了,可以忽略本文。几次解决日志冲突问题时对这几个概念的简单总结,希望对这块基础没有理解透的同学能有所帮助,当然如果对...

飓风2000
2017/10/24
0
0
为什么要使用 SLF4J 而不是 Log4J

本文由 ImportNew - Jaskey 翻译自 javarevisited。如需转载本文,请先参见文章末尾处的转载要求。 每一个Java程序员都知道日志对于任何一个Java应用程序,尤其是服务端程序是至关重要的,而...

编走编想
2013/12/07
0
0
25行代码实现一个简单的编译器

起因 《25行JavaScript语句实现一个简单的编译器》实现的是一个简单到不能再简单的玩具的玩具,他的魔法是函数式编程简化了js代码。java 8提供了函数式编程的支持,昨晚脑子抽风突然兴趣jav...

obaniu
2017/11/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

golang使用protobuf简易教程

参考文档:https://blog.csdn.net/qq_15437667/article/details/78425151 一、安装protobuf # 去github.com/golang/protobuf下载源码包,# 拷贝到 $GOPATH/src/github.com/golang/protobuf......

科陆李明
昨天
0
0
8月16日 上课截图

小丑鱼00
昨天
0
0
Nginx负载均衡、配置SSL

Nginx负载均衡 在 /usr/local/nginx/conf/vhost/ 下创建一个文件,写入以下内容 加载后用curl测试可以访问设置的网站 www.qq.com ssl原理 HTTPS是一种加密的http协议,如果HTTP通信的数据包在...

黄昏残影
昨天
0
0
String 源码阅读笔记

String源码阅读 本人学习笔记,内容来自于阅读源码和其他博客,水平有限,如有错误,烦请指正。 详情参考: Java 7 源码学习系列(一)——String 请别再拿“String s = new String("xyz");...

等到烟火清凉_
昨天
4
0
Coding and Paper Letter(十二)

资源整理。<!-- more --> 1 Coding: 1.R语言生成的ppt,GeoStat2018会议报告,时空模式分析的报告。 geostat18 2.欧空局哨兵和SMOS的工具集,关于对地观测数据的处理与分析的docker容器。 ...

胖胖雕
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部