[ACCEPTED]-Java Logging: show the source line number of the caller (not the logging helper method)-apache-commons-logging
Alternative answer.
It is possible to ask 9 log4j to exclude the helper class by using 8 the method
Category.log(String callerFQCN, Priority 7 level, Object message, Throwable t)
and specifying 6 the helper class as 'callerFQCN'.
For example 5 here is a class using a helper:
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");
}}
and the code 4 of the helper:
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);
} }
The first method will output 3 your expected result.
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
When using this method, Log4j 2 will work as usual, avoiding calculating 1 the stack trace if it is not required.
Please note that giving the line number 6 is something very costly, either for what you get naturally 5 from Log4j or the following. You have to 4 accept that cost...
You could use the following 3 APIs:
StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = ...;
stackTraceElement.getLineNumber();
Updated:
You would have to calculate 2 it yourself. So:
- ask log4j not to output it (in your logging format),
- and insert yourself the line number explicitement in the beginning of your message (the String you send to log4j).
Depending how you prefer 1 your loggers, your helper method may:
- use an explicit Logger (passed as a parameter I guess), when appropriate (we sometimes define specific loggers for specific context ; for example, we have a logger for sending our database requests, no matter what class does it ; this allow us to reduce to one place the changes made to our configuration file, when we want to (de-)activate them ...)
- use a Logger for the calling class : in this case, instead of passing the parameter, you can deduce the caller class name likewise...
Comes out that there is a very simple solution, just 3 add FQCN (The wrapper class' fully qualified 2 class name) to your 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...
In Your 1 working class you just do:
public class MyClass {
private static final Logger LOG = MyLogger.getLogger();
private void test()
{
LOG.info("test");
}
}
Adding details to KLE answer. (sorry, noob 11 user, don't know better way than creating 10 a separate answer )
Instead of sticking the 9 line number to the message, you can put 8 it in the MDC context. See org.apache.log4j.MDC
For 7 example:
StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = ...;
int l = stackTraceElement.getLineNumber();
MDC.put("myLineNumber", l);
That allows users to use mylineNumber 6 in their log4j configuration file
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="Line(%X{myLineNumber})- %m%n"/>
</layout>
Note: that 5 allows the user to control where and how 4 the line number appears in the message. However, since 3 getting the stacktrace is very costly, you 2 still need to find a way to switch off the 1 feature.
For Log4j2 the answer is provided completely 18 by the use of logger wrappers as described 17 in the Log4j2 manual under Example Usage of a Generated Logger Wrapper. One can simply 16 generate (using the org.apache.logging.log4j.core.tools.Generate$ExtendedLogger 15 tools illustrated there) a logger wrapper 14 with a single STUB level, and then adapt 13 that to create custom logging methods mimicking 12 the use of the logIfEnabled(FQCN, LEVEL, Marker, message, Throwable) - possibly 11 ignoring the STUB level and using the regular 10 ones - then if desired, deleting or commenting 9 out the STUB level and its methods). For 8 this purpose the FormattedMessage can be 7 helpful.
The source line, while expensive, can 6 then be easily shown as part of the full 5 location information by using the %l location 4 conversion pattern element in the PatternLayout given 3 in the configuration, or more specifically 2 using the %L line number and/or the %M method 1 conversion.
Now with complete example at: Java Logging: Log4j Version2.x: show the method of an end-client caller (not an intermediate logging helper method)
This isn't possible out of the box. The 4 best you can do in this case is to create 3 the logger in the caller and pass it to 2 the util method. This way, you can at least 1 get an idea where the call has come from.
If you have your own logging utility methods, you 6 could add linenumber and filename to the 5 logging argument list and take the cpp route. i.e. Preprocess 4 you source to replace tags like _ LINE _ and 3 _ FILE _ before you do the compile. As an added 2 bonus this would not take nerly as much 1 resources as figuring out at runtime.
Maybe you can implement the log helper function 8 using the stack trace element, get the line 7 numbers, and bypass the frames with method 6 with some specific annotations, like,
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());
}
Thus, if 5 the helper function is nested, or there 4 are more utilized helper functions, just 3 mark the SkipFrame annotation on all of 2 them and you will get the correct source 1 line number what you really wanted.
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.