当前位置: 首页 > 知识库问答 >
问题:

Log4j2:如何在不使用Throwable的情况下获取类名和行号?

傅博容
2023-03-14

我在Log4j2上开发了一个包装器类。使用OSGi的声明性服务,我发布了一个定制的记录器服务,使用我自己的记录器接口,包装器类作为实现。包装器类仅用于以编程方式配置记录器,即消息格式

我想打印日志文件中请求的每个日志的源类/文件名和行号。选项%C/%F和%L只打印有关包装器类中我实际调用log方法的位置的信息。

因此,作为一种锻炼,我每次都把新的Throwable作为参数传递,这样我就可以使用布局%Throwable{short.lineNumber}。但对于嵌入式应用程序来说,这是一个昂贵的过程。

我的主要问题是获取行号,因为对于文件名,我至少可以从Log4j2请求一个新的记录器,其中包含请求记录器服务的每个服务的名称,并将其保存在地图中。

有没有办法追踪来电者?我希望有一个类似的解决方案,适用于那些不想让logger服务的每个消费者都使用LOG4j2 JAR的应用程序。仅供参考,我不想使用任何XML文件,所有配置都是以编程方式进行的。

共有3个答案

汪甫
2023-03-14

正如@Remko Popma所说,这里有一个简单的实现:
首先创建ExtendedLogger类:

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;

public class ExtLogWithLine extends ExtendedLoggerWrapper {
    private static final long serialVersionUID = 8239280349129059055L;

    // define your wrapper class here
    private static final String FQCN = WrapperLog.class.getName();

    private final ExtendedLoggerWrapper logger;

    private ExtLogWithLine(final Logger logger) {
        super((AbstractLogger) logger, logger.getName(), logger.getMessageFactory());
        this.logger = this;
    }

    public static ExtLogWithLine create(final String name) {
        final Logger wrapped = LogManager.getLogger(name);
        return new ExtLogWithLine(wrapped);
    }

    @Override
    public void debug(String info) {
        if (isDebugEnabled()) {
            logger.logIfEnabled(FQCN, Level.DEBUG, null, info, (Throwable) null);
        }
    }

    @Override
    public void info(String info) {
        if (isInfoEnabled()) {
            logger.logIfEnabled(FQCN, Level.INFO, null, info, (Throwable) null);
        }
    }

    @Override
    public void warn(String info) {
        if (isWarnEnabled()) {
            logger.logIfEnabled(FQCN, Level.WARN, null, info, (Throwable) null);
        }
    }

    @Override
    public void error(String info) {
        if (isErrorEnabled()) {
            logger.logIfEnabled(FQCN, Level.ERROR, null, info, (Throwable) null);
        }
    }
}
public class WrapperLog {

    public void testLog() {
       ExtLogWithLine logger = ExtLogWithLine.create("xxx");
       
       // log will print out with correct line number
       logger.info("see the line number");
    }
}
吴举
2023-03-14

Log4j2在内部遍历stackTrace以提取位置信息,它通过向org.apache.logging.log4j.spi.ExtendedLogger#logMessage方法指定正确的FQCN(完全限定类名)来知道在堆栈跟踪中停止的位置。

Log4j2包含一个为记录器包装器生成代码的工具。文档如下(在自定义日志级别手册页面中):http://logging.apache.org/log4j/2.x/manual/customloglevels.html#CustomLoggers

尝试使用此工具生成记录器包装,并基于生成的代码创建自定义包装。此生成的代码将使用正确的FQCN,并能够生成正确的位置信息。

牛华皓
2023-03-14

你可以用

StackTraceElement[] stes = Thread.currentThread().getStackTrace();

然而,我不确定这是否便宜得多。

我要做的是使每一条消息都是唯一的(对于这个类),并避免包含行号。您可以在IDE中搜索唯一的消息以查找行号。该类应使用记录器的名称。

 类似资料:
  • 问题内容: 在C#中,我们有和有或没有命名空间(包在Java世界中)得到一个类型(类在这种情况下)的名称。 java等于什么? 显然,有比手动使用和删除软件包名称更好的方法。 问题答案: 返回源代码中给定的基础类的简单名称。如果基础类是匿名的,则返回一个空字符串。 数组的简单名称是组件类型的简单名称,后接“ []”。特别是其组件类型为匿名的数组的简单名称为“ []”。 它实际上是从名称中删除软件包

  • 首先,如果这是一个很长的代码段,我很抱歉,但是,我想做一个模态窗口,它将你在我的用户表单中写的东西写下来,并要求你确认它。我目前正在学习Javascript,我不允许使用innerHTML,我必须动态地编写“名字”等(名字的文本),不允许只在弹出窗口内写它。我已经让大多数东西工作,但“名字”“名字”等显示为“未定义”,或者(正如你可以看到的,我在这种情况下只用名字尝试的事情)显示为“空”。 希望有

  • 我有一个正在使用Azure AD身份验证的应用程序。我还需要访问Microsoft Graph API以获取用户数据。我发现的每个向Graph API发出请求的示例都使用了缓存的会话令牌,但由于我使用的是JWT,因此显然不需要存储会话状态。如何使用JWT将我的应用程序作为受众来获得具有适当受众的JWT? 例如,这里有一个从Microsoft Graph AspNetCore示例检索令牌的请求: 它

  • 我们能在不使用互联网的情况下获取设备当前的纬度和经度吗?我使用以下代码。。单击按钮后,我将触发位置侦听器。 当它连接到internet时,可以很好地获取经度和纬度值,但当它未连接到internet时,返回null。 有什么问题吗?LocationManager.NETWORK_PROVIDER,5000,20, locationListener需要互联网来获取延迟和长时间吗? 如果需要互联网,那么

  • 尝试使用Request.GetParameter但结果检索到第一个值,即第一个索引: 将名称取为“MyArray[]”,并在下一页使用 访问该值` 现在我应该做什么,有没有替代方法或者要在这里做一些更改: 这是page One.jsp中的表单: 下面是代码片段: 注意:忽略计数器值,因为这里只是为了检查,代码运行正常,但它只使用Request.GetParameter检索第一个索引值。 请给我一些

  • 我有一个带有复合骆驼用例名称的Java实体,例如。 我需要将这些实体映射到带有snake case名称的表上,例如,而不是Quarkus/Panache使用的默认值,例如。 我知道我可以用设置自定义表名,但我想知道是否有某种应用程序属性为我做同样的工作。