Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java日志记录:显示调用者的源行号(不是日志记录助手方法)_Java_Logging_Log4j_Apache Commons Logging - Fatal编程技术网

Java日志记录:显示调用者的源行号(不是日志记录助手方法)

Java日志记录:显示调用者的源行号(不是日志记录助手方法),java,logging,log4j,apache-commons-logging,Java,Logging,Log4j,Apache Commons Logging,众多(叹气…)Java日志框架都很好地显示了创建日志消息的方法的源文件名行号: log.info("hey"); [INFO] [Foo:413] hey 但是,如果中间有一个helper方法,那么实际的调用方将是helper方法,这不是太多信息 log_info("hey"); [INFO] [LoggingSupport:123] hey 在确定要打印的源位置时,是否有方法告诉日志记录系统从调用堆栈中删除一个帧 我认为这是特定于实现的;我需要的是通过Commons日志记录的Log4

众多(叹气…)Java日志框架都很好地显示了创建日志消息的方法的源文件名行号:

log.info("hey");

 [INFO] [Foo:413] hey
但是,如果中间有一个helper方法,那么实际的调用方将是helper方法,这不是太多信息

log_info("hey");

[INFO] [LoggingSupport:123] hey
在确定要打印的源位置时,是否有方法告诉日志记录系统从调用堆栈中删除一个帧


我认为这是特定于实现的;我需要的是通过Commons日志记录的Log4J,但我有兴趣了解其他选项。

请注意,提供行号是非常昂贵的,无论是从Log4J还是以下内容中自然获得的。你必须接受这个代价

您可以使用以下API:

    StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
    StackTraceElement stackTraceElement = ...;
    stackTraceElement.getLineNumber();

更新:

你得自己计算。因此:

  • 要求log4j不要输出它(以您的日志格式)
  • 并在消息的开头插入行号(发送到log4j的字符串)
根据您对记录器的偏好,您的助手方法可能会:

  • 在适当的时候使用显式记录器(我想是作为参数传递的)(我们有时会为特定的上下文定义特定的记录器;例如,我们有一个用于发送数据库请求的记录器,不管它是什么类;这允许我们在需要时将对配置文件所做的更改减少到一个位置(de-)激活它们…)
  • 为调用类使用记录器:在这种情况下,您可以推断调用方类名,而不是传递参数

    • 这是不可能开箱即用的。在这种情况下,您最好在调用者中创建记录器,并将其传递给util方法。这样,你至少可以知道电话是从哪里打来的。

      为应答添加详细信息。(对不起,noob用户,不知道比创建单独答案更好的方法)

      您可以将其放在MDC上下文中,而不是将行号粘贴到消息上。请参见org.apache.log4j.MDC

      例如:

      StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
      StackTraceElement stackTraceElement = ...;
      int l = stackTraceElement.getLineNumber();
      
      MDC.put("myLineNumber", l);
      
      这允许用户在其log4j配置文件中使用mylineNumber

      <layout class="org.apache.log4j.PatternLayout">
          <param name="ConversionPattern" 
                 value="Line(%X{myLineNumber})- %m%n"/>
      </layout>
      
      
      
      注意:这允许用户控制行号在消息中显示的位置和方式。但是,由于获取stacktrace的成本非常高,因此仍然需要找到关闭该功能的方法

      备选答案

      可以要求log4j使用该方法排除helper类

      log(字符串callerFQCN、优先级、对象消息、Throwable t)

      并将助手类指定为“callerFQCN”

      例如,下面是一个使用帮助器的类:

      public class TheClass {
          public static void main(String...strings) {
              LoggingHelper.log("Message using full log method in logging helper.");
              LoggingHelper.logNotWorking("Message using class info method");
      }}
      
      以及助手的代码:

      public class LoggingHelper {
      private static Logger LOG = Logger.getLogger(LoggingHelper.class);
      
      public static void log(String message) {
          LOG.log(LoggingHelper.class.getCanonicalName(), Level.INFO, message, null);
      }
      
      public static void logNotWorking(String message) {
          LOG.info(message);
      } }
      
      第一个方法将输出预期结果

      Line(TheClass.main(TheClass.java:4)) Message using full log method in logging helper. Line(LoggingHelper.logNotWorking(LoggingHelper.java:12)) Message using class info method 行(class.main(class.java:4))消息,在logging helper中使用完整日志方法。 使用类信息方法生成一行(loggingheloper.logNotWorking(loggingheloper.java:12))消息
      使用此方法时,Log4j将正常工作,避免在不需要时计算堆栈跟踪。

      如果您有自己的日志实用程序方法,则可以将行号和文件名添加到日志参数列表中,并采用cpp路由。i、 e.在编译之前,对源代码进行预处理,以替换诸如uLine和FILE之类的标记。作为一个额外的好处,这不会像在运行时计算那样占用太多的资源。

      也许您可以使用stack跟踪元素实现log helper函数,获取行号,并使用带有一些特定注释的方法绕过框架,如

      public @interface SkipFrame {}
      
      // helper function
      @SkipFrame // not necessary on the concrete log function
      void log(String... message) {
          // getStackTrace()...
          int callerDepth = 2;  // a constant number depends on implementation
          StackTraceElement callerElement = null; 
          for (StackTraceElement e: stackTrace) {
               String className, methodName = e.getClassName, getMethodName()...
               Class callClass = Class.forName(className);
               // since there maybe several methods with the same name
               // here skip those overloaded methods
               Method callMethod = guessWhichMethodWithoutSignature(callClass, methodName);
               SkipFrame skipFrame = callMethod.getAnnotation(SkipFrame.class); 
               if (skipFrame != null)
                   continue; // skip this stack trace element
               if (callerDepth-- == 0) {
                   callerElement = e; 
                   break;
               }
           }
           assert callerDepth == 0; 
           assert callerElement != null;
           Log4j.info(callerElement.getLineNumber()... + "message... "); 
      }
      
      @SkipFrame
      void logSendMail(Mail mailObject) {
          log("Send mail " + mailObject.getSubject()); 
      }
      

      因此,如果helper函数是嵌套的,或者有更多使用的helper函数,只需在所有这些函数上标记SkipFrame注释,就可以得到您真正想要的正确源代码行号

      有一个非常简单的解决方案,只需将(包装类的完全限定类名)添加到您的logger helper:

      public class MyLogger extends Logger {
      
      private static final String FQCN = MyLogger.class.getName() + ".";
      
      protected MyLogger(String name) {
          super(name);
      }
      
      public void info(final Object msg) {
          super.log(FQCN, Level.INFO, msg, null);
      }
      
      //etc...
      
      在你的工人阶级,你只需要做:

      public class MyClass {
      
      private static final Logger LOG = MyLogger.getLogger();   
      
      private void test()
      {
          LOG.info("test");
      }
      
      }
      

      对于Log4j2,答案完全通过使用Log4j2手册中所述的记录器包装器提供。您只需生成(使用org.apache.logging.log4j.core.tools.generate$ExtendedLogger工具,如图所示)一个具有单个存根级别的记录器包装器,然后对其进行调整,以创建模仿使用logIfEnabled(FQCN、级别、标记、消息、可丢弃)的自定义日志方法-可能忽略存根级别并使用常规的存根级别-如果需要,删除或注释存根级别及其方法)。为此,格式化消息可能会有所帮助

      源代码行虽然昂贵,但是可以通过使用配置中给定的%l位置转换模式元素,或者更具体地使用%l行号和/或%M转换方法,轻松地显示为完整位置信息的一部分


      现在有完整的例子:

      我想Log4J正在做类似的事情。我想做的是告诉它在计算中跳过另一帧。我可以自己做所有的计算,但我仍然需要以某种方式将这些信息传递到日志系统,否则日志格式选项将无法识别我的奇特行号。是的,你必须自己计算。因此,请log4j不要输出它(以您的日志格式),并在消息的开头插入行号说明(发送给log4j的字符串)。(我将此添加到答案中)现代JVM的成本要低得多。您真的建议您应该在每个记录的消息上手动插入行号吗?这不是很容易维护的。事实上,如果我在分配给我的项目中看到这个,我会辞职。是的,这会给我正确的记录器类别名称(但仍然是错误的行号)。这是一个进步…我想也许