Separate loggers for different methods - java

I use loggers to print traces that are specific to certain methods. That way when I want to debug my code I can activate traces in only those methods I care about.
I used to be able to do that with log4j v1. I would instrument selected methods with loggers like this:
private static void method1() {
Logger logger = Logger.getLogger("org.examples.method1");
logger.debug("Hello from method1");
etc...
}
private static void method2() {
Logger logger = Logger.getLogger("org.examples.method2");
logger.debug("Hello from method2");
etc...
}
Then in my log4j.properties file, I would activate the traces like this:
log4j.logger.org.examples.method1=debug
log4j.logger.org.examples.method2=debug
etc...
I am trying to replicate that behavior in log4j v2, but am having problems with it.
As far as instrumenting methods with loggers, I am doing OK. I do it the same way as with v1, except that I use LogManager.getLogger() instead of Logger.getLogger() (which does not exist in v2).
For activating the loggers, I need to use the v2 syntax, so I now do this:
logger.app.name = org.examples.method1
logger.app.level = debug
logger.app.name = org.examples.method2
logger.app.level = debug
The problem is that when I run code that invokes both method1 and method2, only the method2 traces appear. In order to see the method1 traces, I have to comment out the method2 properties. Note that I have tried adding the additivity = true to both loggers, to no effect.
Piotr (see answer below) says that I need to have a different value for the '.app' bit in the above code. So instead I should write something like this:
logger.1.name = org.examples.method1
logger.1.level = debug
logger.2.name = org.examples.method2
logger.2.level = debug
But this is really inconvenient for my use case because I have to:
Make sure I use the same number on all lines that pertain to a given logger
Make sure that I never use the same number for a different logger
One solution would be for me to write a macro that would automatically generate the two lines from the name of the logger. So, for a logger called org.examples.method1, it might generate:
logger.org_examples_method1.name = org.examples.method1
logger.org_examples_method1.level = debug
Pietr also suggested that I might use the .yml or .json syntax instead of .properties. But when I tried the .yml syntax, I had the same problem, namely, if I write this:
Loggers:
logger:
- name: org.examples.method1
level: debug
logger:
- name: org.examples.method2
level: debug
Then only the method2 traces are being printed. And here, there does not seem to be a "second id" involved, so I am not sure how to fix it.

As you remarked the properties format is not the easiest one to use, since it requires at least to lines per logger and plenty useless identifiers to generate. By comparison you have:
in the properties format:
logger.<id1>.name=org.example.foo
logger.<id1>.level=INFO
logger.<id2>.name=org.example.bar
logger.<id2>.level=DEBUG
in the XML format:
<Loggers>
<Logger name="org.example.foo" level="INFO"/>
<Logger name="org.example.bar" level="DEBUG"/>
</Loggers>
in the YAML format:
Loggers:
logger:
- name: org.example.foo
level: INFO
- name: org.example.bar
level: DEBUG
in the JSON format:
"loggers": {
"logger": [
{ "name": "org.example.foo", "level": "INFO" },
{ "name": "org.example.bar", "level": "DEBUG" }
]
}
There has been some propositions recently aimed at shortening the properties format: see LOG4J2-3341, LOG4J2-3394 or my own PR #735.

Related

How do I add fields to log4j2's JSON logs

Say I have a standard JSON log as in the example in the docs (below)
{
"logger":"com.foo.Bar",
"timestamp":"1376681196470",
"level":"INFO",
"thread":"main",
"message":"Message flushed with immediate flush=true"
}
Now I want to add custom info to this log like so:
{
"logger":"com.foo.Bar",
"timestamp":"1376681196470",
"level":"INFO",
"thread":"main",
"message":"Message flushed with immediate flush=true",
"extrainformation":"Some very important stuff I need to include",
"extrainformation2":"Some other very important stuff I need to include"
}
Is there a way to do this? The docs don't seem to mention anything about adding properties to the log object. Do I need to make a custom layout or programmatically add fields or what?
relevant log4j2 docs
This can be archived simply via the the config file.
See my log4j2.yaml:
Configuration:
status: warn
appenders:
Console:
name: STDOUT
JsonLayout:
complete: false
compact: true
eventEol: true
KeyValuePair:
-
key: extrainformation
value: Some very important stuff I need to include
-
key: extrainformation2
value: Some other very important stuff I need to include
Loggers:
Root:
level: "warn"
AppenderRef:
ref: STDOUT
Custom fields are always last, in the order they are declared. The values support lookups
Like #alan7678 said -
A custom layout was also the solution for me.
#Plugin(name = "ExtendedJsonLayout", category = Node.CATEGORY,
elementType = Layout.ELEMENT_TYPE, printObject = true)
public class ExtendedJsonLayout extends AbstractJacksonLayout {
// Lots of code!
}
I created a Log4j2 layout plugin called "extended-jsonlayout". You can include it in your project with Maven or Gradle. Check it out here -
https://github.com/savantly-net/log4j2-extended-jsonlayout

method name not printing correctly in log4j logger in Java

My log happens in getParam() method. but it is logged as main method. And I have three objects, but I do not see any information as to which object is logging. I want that information too.
Here is the output:
INFO [main]: info
INFO [main]: debug
INFO [main]: trace
here is the code:
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
class Log4jTest {
private static Logger log=Logger.getLogger(Log4jTest.class);
private String param;
public Log4jTest(String param){
this.param=param;
}
public String getParam(){
log.info(param);
return param;
}
}
public class Log4j_testing {
public static void main(String[] args) throws FileNotFoundException{
Log4jTest l1= new Log4jTest("info");
l1.getParam();
Log4jTest l2= new Log4jTest("debug");
l2.getParam();
Log4jTest l3= new Log4jTest("trace");
l3.getParam();
}
}
my log4j.properties:
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.appender.stdout.layout.ConversionPattern=%-5p [%t]: %m%n
On a different note:
if I have the above commented line uncommented and the last line commented out, I get
11:19:41,586 INFO Log4jTest:19 - info
11:19:41,589 INFO Log4jTest:19 - debug
11:19:41,589 INFO Log4jTest:19 - trace
It gives the correct line number(19) that is being logged, but not the correct method.
Add %M to your pattern. However, as it is stated in the JavaDoc: "Generating caller location information is extremely slow and should be avoided unless execution speed is not an issue. "
There are some cases when some patterns are ignored or never logged (%C, %F, %l, %L, %M). This is true when using asynchronous loggers and asynchronous appenders. Here is what the user guide for Log4j 2.x has to said about this (see page 217-218):
If one of the layouts is configured with a location-related attribute
like HTML locationInfo, or one of the patterns %C or %class, %F or
%file, %l or %location, %L or %line, %M or %method, Log4j will take a
snapshot of the stack, and walk the stack trace to find the location
information.
This is an expensive operation: 1.3 - 5 times slower for
synchronous loggers. Synchronous loggers wait as long as possible
before they take this stack snapshot. If no location is required, the
snapshot will never be taken.
However, asynchronous loggers need to
make this decision before passing the log message to another thread;
the location information will be lost after that point. The
performance impact of taking a stack trace snapshot is even higher for
asynchronous loggers: logging with location is 30-100 times slower
than without location. For this reason, asynchronous loggers and
asynchronous appenders do not include location information by default.
You can override the default behaviour in your logger or asynchronous
appender configuration by specifying includeLocation="true".
The doc for PatteryLayout tells us that %M will provide the method name where the logging request was made, but I have not ever been able to get it to work and ended up putting the method name in the log message.

Log statements in JAVA [duplicate]

What is the difference between logger.debug and logger.info ?
When will logger.debug be printed?
I suggest you look at the article called "Short Introduction to log4j". It contains a short explanation of log levels and demonstrates how they can be used in practice. The basic idea of log levels is that you want to be able to configure how much detail the logs contain depending on the situation. For example, if you are trying to troubleshoot an issue, you would want the logs to be very verbose. In production, you might only want to see warnings and errors.
The log level for each component of your system is usually controlled through a parameter in a configuration file, so it's easy to change. Your code would contain various logging statements with different levels. When responding to an Exception, you might call Logger.error. If you want to print the value of a variable at any given point, you might call Logger.debug. This combination of a configurable logging level and logging statements within your program allow you full control over how your application will log its activity.
In the case of log4j at least, the ordering of log levels is:
DEBUG < INFO < WARN < ERROR < FATAL
Here is a short example from that article demonstrating how log levels work.
// get a logger instance named "com.foo"
Logger logger = Logger.getLogger("com.foo");
// Now set its level. Normally you do not need to set the
// level of a logger programmatically. This is usually done
// in configuration files.
logger.setLevel(Level.INFO);
Logger barlogger = Logger.getLogger("com.foo.Bar");
// This request is enabled, because WARN >= INFO.
logger.warn("Low fuel level.");
// This request is disabled, because DEBUG < INFO.
logger.debug("Starting search for nearest gas station.");
// The logger instance barlogger, named "com.foo.Bar",
// will inherit its level from the logger named
// "com.foo" Thus, the following request is enabled
// because INFO >= INFO.
barlogger.info("Located nearest gas station.");
// This request is disabled, because DEBUG < INFO.
barlogger.debug("Exiting gas station search");
This will depend on the logging configuration. The default value will depend on the framework being used. The idea is that later on by changing a configuration setting from INFO to DEBUG you will see a ton of more (or less if the other way around) lines printed without recompiling the whole application.
If you think which one to use then it boils down to thinking what you want to see on which level. For other levels for example in Log4J look at the API, http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Level.html
Just a clarification about the set of all possible levels, that are:
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
Basically it depends on how your loggers are configured. Typically you'd have debug output written out during development but turned off in production - or possibly have selected debug categories writing out while debugging a particular area.
The point of having different priorities is to allow you to turn up/down the level of detail on a particular component in a reasonably fine-grained way - and only needing to change the logging configuration (rather than code) to see the difference.
INFO is used to log the information your program is working as expected.
DEBUG is used to find the reason in case your program is not working as expected or an exception has occurred. it's in the interest of the developer.
This is a very old question, but i don't see my understanding here so I will add my 2 cents:
Every level corresponds/maps to a type of user:
debug : developer - manual debugging
trace : automated logging and step tracer - for 3rd level support
info : technician / support level 1 /2
warn : technician / user error : automated alert / support level 1
critical/fatal : depends on your setup - local IT
It depends on which level you selected in your log4j configuration file.
<Loggers>
<Root level="info">
...
If your level is "info" (by default), logger.debug(...) will not be printed in your console.
However, if your level is "debug", it will.
Depending on the criticality level of your code, you should use the most accurate level among the following ones :
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
Info Messages are something which we would like to see even if the application is in best of state.
Debug messages are usually something that we would like to see while debugging some problem.
What is the difference between logger.debug and logger.info?
These are only some default level already defined. You can define your own levels if you like.
The purpose of those levels is to enable/disable one or more of them, without making any change in your code.
When logger.debug will be printed ??
When you have enabled the debug or any higher level in your configuration.

Formatting slf4j to log message types with colors

I am using slf4j for logging in my Java Application. It involves a lot logging and log monitoring.
Sometimes it is really difficult to read and find information from logs when the entire log is printed in the black color with.
Just to make it more readable, is it possible to log the different kinds of messages in different colors?
For example all Error level messages in Red color or a different font size and all Info level messages in blue color and a different font size.
Any suggestions or help is welcome.
Thnx.
It's not possible to change colors of slf4j logging, because there are no formatters. SLF4J is a middleware between your application and some logging facility, for example, Log4j or Logback.
You can change colors in Log4j output, as explained here. I would recommend to use MulticolorLayout from jcabi-log
Something you have to bear in mind.
First, SLF4J is only a logging facade. How the actual log message is handled depends on the binding it used. Therefore your question is invalid, instead, you should quote which implementation you want to use (LogBack? Log4J? etc)
Second, "Coloring" is not something meaningful in most case. For example, if you are referring a plain text log file, there is nothing that we can control the coloring because they are all plain text (unless your editor have special syntax highlighting built-in for your log message format). It may be meaningful if you want to see the color in the console/terminal, or if you are outputting your log into file format that allow you to contain color information (e.g. HTML).
With these two idea in mind, here is my suggestion.
LogBack has built-in support for coloring http://logback.qos.ch/manual/layouts.html#coloring in console output. If you are looking for way to see color in console output, and you are allowed to use LogBack, this is what you are looking for.
Two solutions come to my mind. They are not colors, but alternative solutions:
In your File Appender configuration, you can configure the pattern to include the log level (error, warn, etc). Then you can grep the file, to filter messages by level.
You can configure two file appenders (for two separate log files) with different level threshold. For instance, one would log all the logs above debug level (so info, warn, error) into let's say logs.txt and the other would log only the errors logs into errors.txt
Hope it helps.
Add next appender into logback.xml to colorize logs output:
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<Pattern>%d %highlight(%-5level) [%thread] %cyan(%logger{15}): %msg%n</Pattern>
</encoder>
</appender>
In addition to using a colored ConsoleAppender, you could also color the statement itself. Note this has limitations and may not work with all systems. It worked well for internal projects, particularly when you want to log that a major task in a chain is complete or simply while debugging errors.
To quote an external link:
System.out.println("\u001B[31m" + "the text is red" + "\u001B[0m");
\u001B[31m is the ANSI code to color the output red and \u001B[31m is the ANSI reset command.
public class ColorLogger {
private static final Logger LOGGER = LoggerFactory.getLogger(ColorLogger.class);
public void logDebug(String logging) {
LOGGER.debug("\u001B[34m" + logging + "\u001B[0m");
}
public void logInfo(String logging) {
LOGGER.info("\u001B[32m" + logging + "\u001B[0m");
}
public void logError(String logging) {
LOGGER.error("\u001B[31m" + logging + "\u001B[0m");
}
}
In your case, it sounds like you can try to write the contents of the aggregated log out and optionally color each statement by finding each string that concerns you.
This can alternatively just be done by printing it to an HTML page and using CSS selectively on your text.
I use filters for both logging level and package. This example comes from Spring boot application.properties
logging.level.root=warn
logging.level.org.springframework=warn
logging.level.org.hibernate=warn
logging.level.org.starmonkey.brown=DEBUG
This way I see only messages I want to see

Logger.setLevel() doesn't enable logging correctly

Situation: I have this log4j logger:
private static final Logger logger = Logger.getLogger(ThisClassName.class);
And am trying to set it programatically through:
Logger.getLogger(ThisClassName.class).setLevel(Level.DEBUG);
Still, DEBUG level prints are swalloed (while INFO prints are printed successfully).
Even this bit has no effect: Logger.getRootLogger().setLevel(Level.DEBUG);
Calling logger.debug("foo") reaches Category.forcedLog() and ConsoleAppender.doAppend(), and then fails (quits) at:
if(!isAsSevereAsThreshold(event.getLevel()))
Any idea why this is happening?
Your appender is configured with a threshold greater than debug, so while the logger doesn't ignore the entries, your appender doesn't record it. You need to configure the threshold of your ConsoleAppender to be DEBUG as well, either through your config file or programatically:
((ConsoleAppender)someLogger.getAppender("CONSOLE")).setThreshold(Level.DEBUG);
Config files are usually the more elegant solution for this sort of thing.
Edit: Note that apparently, any subclass of AppenderSkeleton (including ConsoleAppender) shouldn't have a threshold filter set by default. So it's likely that somewhere in your configuration you're actually manually assigning a threshold ( > Debug) to that appender, as #justkt hints.

Categories