How can I set individual log levels in different Java classes? [duplicate] - java

This question already has an answer here:
How to configure log4j2.xml log level for specific class only?
(1 answer)
Closed 4 years ago.
(I' new to Java, and I read java.util.logging: how to set level by logger package (or prefix)? already. As it couldn't answer my question, here it is)
I'm developing a system that has private static final Logger log = Logger.getLogger(XXX.class.getName()); attributes in several classes (XXX being the corresponding class).
I use log.setLevel(level) with different levels, but all the log objects seem to work at the same log level. Printing the log itself, seems to indicate that the individual log objects are actually the same. I don't understand why.
Debug output is like this:
[CONFIG ]...Parser init: java.util.logging.Logger#6bc7c054 logging level FINE
[CONFIG ]...Tokenizer init: java.util.logging.Logger#6bc7c054 logging level CONFIG
And still the Parser class logs at level CONFIG...

Printing the log itself, seems to indicate that the individual log objects are actually the same. I don't understand why.
Parser and Tokenizer are calling Logger.getLogger(XXX.class.getName()); with the same name XXX class. Modify your code example to print the name of the logger.
Debug output is like this
This is where a Minimal, Complete, and Verifiable example helps me. Levels are used to qualify messages and levels are used to filter messages. If you don't include at least the code that produced your debug output it is hard to tell what the meaning is for each level listed.

You appear to be confusing the Logger's filtering level and a message's logging level.
Each logger has a logging level that acts as a filter for messages;
messages that are logged at a level which is lower than the filtering level of the logger are ignored.
Here is an example (not actual code):
Logger myLogger = Logger.getLogger("somename");
myLogger.setLevel(FINE);
myLogger.fine("fine grained log message");
myLogger.finest("finest level of logging");
myLogger.info("info level message");
myLogger.fine("second fine message");
The "code" above will produce the following messages in the log file:
fine grained log message
info level message
second fine message
Note that the message "finest level of logging" will not appear in the log because FINEST level is lower than FINE and is thus filtered out by the logger.

Related

Does "Logging with slf4j at warn level", prevent the running "logger.debug(..."

i've a question about processing log lines according to level like;
logger.debug("hello i am a log line not so neccessary, also log level is setted WARN and some complexProcessResult-> {}",
doSomeWorkwhichTakeslongTime());
If logger level sets to WARN so does it call the process "doSomeWorkwhichTakeslongTime()" because jsonizing some classes, take so much time, and i dont want to run this in production. To achieving this, is it enough setting the log level as "warn"?
No, if you use this specific method call, then changing the log level would have no impact on whether or not doSomeWorkWhichTakesLongTime() is being called or not.
It can't have an effect, because the Java language specifies that the parameter values need to be computed before the method is called.
There are two possible workarounds:
The "ugly" one is to use isDebugEnabled():
if (logger.isDebugEnabled()) {
logger.debug("hello i am a log line not so neccessary, also log level is setted WARN and some complexProcessResult-> {}", doSomeWorkwhichTakeslongTime());
}
The other one is a bit trickier, but potentially nicer: Have the heavy lifting happen in the toString() of an object that is either cheap to construct or that you have accessible anyway:
logger.debug("hello i am a log line not so neccessary, also log level is setted WARN and some complexProcessResult-> {}", someObjectThatDescribesTheOutput)
and have someObjectThatDescribesTheOutput have a toString method a little like this:
public String toString() {
return doSomeWorkwhichTakeslongTime();
}

How to refresh log4j after change of the external class logging level at runtime

I have some warnings which I want to disable. They comes from the
org.eclipse.lyo.oslc4j.provider.jena.JenaModelHelper
class. So I am using log4j and I call at rutime
Logger.getLogger("org.eclipse.lyo.oslc4j.provider.jena.JenaModelHelper").setLevel(Level.OFF);
but nothing happens.
For that purpose I debug the problematic code and first 3 times the warn was displayed, but on the 4-th hit the level was changed.
I changed the log level at the begging(in the constructor) so I suppose there is some refresh mechanism in the log4j when the runtime applied rule is take into account.
Is there a way to trigger this to happen immediately?
Edit:
The warning message is:
WARNING: Property http://localhost:8080/dwa/rm/urn:rational::1-55422447386d3378-M-00000200/types/attrDef-7 could not be parsed as datatype http://www.w3.org/2001/XMLSchema#dateTime
com.hp.hpl.jena.datatypes.DatatypeFormatException: Lexical form '2015-06-10' is not a legal instance of Datatype[http://www.w3.org/2001/XMLSchema#dateTime -> class com.hp.hpl.jena.datatypes.xsd.XSDDateTime] Lexical form '2015-06-10' is not a legal instance of Datatype[http://www.w3.org/2001/XMLSchema#dateTime -> class com.hp.hpl.jena.datatypes.xsd.XSDDateTime] during parse -org.apache.xerces.impl.dv.InvalidDatatypeValueException: cvc-datatype-valid.1.2.1: '2015-06-10' is not a valid value for 'dateTime'.
at com.hp.hpl.jena.graph.impl.LiteralLabelImpl.getValue(LiteralLabelImpl.java:339)
at com.hp.hpl.jena.graph.Node_Literal.getLiteralValue(Node_Literal.java:39)
at com.hp.hpl.jena.rdf.model.impl.LiteralImpl.getValue(LiteralImpl.java:98)
You can disable the logging in you configuration file (log4j.properties), so this will be set at startup already:
log4j.logger.org.eclipse.lyo.oslc4j.provider.jena.JenaModelHelper=ERROR
The warning you posted looks much like a Java Util Logging message, those wont be controled by Log4J because its a different framework. You can either disable the message in your VMs "logging.properties" file:
org.eclipse.lyo.oslc4j.provider.jena.JenaModelHelper.level=SEVERE
Or you can add SLF4J to your project to redirect legacy framework messages to Log4J. To do this put slf4j-api.jar, jcl-over-slf4j.jar, jul-to-slf4j.jar and slf4j-log4j12.jar in your classpath.

Custom Log Level

In my existing application "org.apache.log4j" API's have been used in java code.
Requirement :
I have to log some statement(say xyz) in log file in any case and should not dependent of log levels.For example : if my log level is error then also xyz should print, if my log level is debug then also xyz should print.
I cannot make log statement of xyz is debug because if i do this, other log statements apart from xyz will also start printing.
For this, I believe, I have to add some custom log level.Please help how to do it and how to set its level ordering so that in any case it should print.
Thanks in advance.
Best Regards
What you could do is create a different Logger for those statements (you are not restricted to use classes names when defining a logger)
// Standard logger
private static Logger log = Logger.getLogger(MyClass.class)
// XYZ logger
private static Logger logXYZ = Logger.getLogger("logs.xyz");
You can access the same logger from several class, you just have to pass the same label.
Then, in the configuration file, you can define a different log level for that category, and even output these logs in a different appender (different file, processing, etc.)
You could "hijack" the protected method Logger#forcedLog() to always print to the log.
You must place the hijacker class in the same package as Logger.
package org.apache.log4j;
/**
* #author maba, 2012-08-23
*/
public class LogOverride {
public static void print(Logger logger, String message) {
logger.forcedLog(logger.getName(), Priority.INFO, message, null);
}
}
And from your calling code
log.setLevel(Level.OFF); // Make sure logging is turned off
log.info("Normal logging"); // Will not be seen anywhere
LogOverride.print(log, "Overriding logger"); // Will still make it to your appender
This is what the log4j FAQ says about custom levels:
How do I add a custom level?
It is possible, but rarely appropriate. The request is commonly for a level named something like "audit" that doesn't obviously fit in the progression "trace", "debug", "info", "warn", "error" and "fatal". In that case, the request for a level is really a request for a mechanism to specify a different audience. The appropriate mechanism is to use a distinct logger name (or tree) for "audit" related messages.
So if you want to go with that suggestion then you should look at the answer from SJuan76.
If you do decide to go with the idea of creating a custom, you would need to create a subclass of Level to do this, because the Level constructor is protected.
/**
* Instantiate a Level object.
*/
protected Level(int level, String levelStr, int syslogEquivalent) {
super(level, levelStr, syslogEquivalent);
}
It looks like you should then chain to the Level constructor, passing it a suitable level value. Note that the larger the level number the higher the priority is. So for a Level that won't be blocked at any of the existing named levels, you want a value that is greater than Priority.FATAL_INT which is 50000.
(However, I'm not convinced that this is the right approach. For a start, you probably won't be able to refer to your custom level by name in a logging config file.)

Change global setting for Logger instances

I'm using java.util.logging.Logger as the logging engine for my application. Each class uses it's own logger, i.e., each class has:
private final Logger logger = Logger.getLogger(this.getClass().getName());
I want to set a logging level for all my classes, and be able to change it (i.e., have the setting in one place). Is there a way to do this, other that using a global Level variable and manually set each logger to it?
One easy way is to use a logging properties file, by including this VM argument:
-Djava.util.logging.config.file="logging.properties"
where "logging.properties" is the path to a file containing logging configuration. For relative paths, the working directory of the process is significant.
In that file, include a line like this:
.level= INFO
This sets the global level, which can be overridden for specific handlers and loggers. For example, a specific logger's level can be overridden like this:
com.xyz.foo.level = SEVERE
You can get a template for a logging properties file from jre6\lib\logging.properties.
As Andy answered, in most cases you should use the property file and the VM argument, thus its independent from your code.
But if you want to go programatically for some reason (I myself had a good reason in one case) you can access the Handlers like this too:
Logger rootLogger = LogManager.getLogManager().getLogger("");
rootLogger.setLevel(Level.INFO);
for (Handler h : rootLogger.getHandlers()) {
h.setLevel(Level.INFO);
}
EDIT I added the setLevel to the root logger as searchengine27 pointed out in in his answer.
The Handlers are File or Console Handlers that you setup via the properties or programatically too.
Or change filters like this:
Logger rootLogger = LogManager.getLogManager().getLogger("");
rootLogger.setFilter(new Filter() {
#Override
public boolean isLoggable(LogRecord record) {
return "something".equals(record.getLoggerName());
}
});
So I don't entirely like all of the answers here, so I'm going to chime in.
Config file use
You're seeing a lot of answers in here telling you to use the config file because it is best practice. I want to explain better how to do this programatically, but before I do, I want to say that I can see where they are coming from, and in the mood of being objective, I will enlighten you a bit (especially because nobody says why its bad practice). I actually want to share what somebody said in a separate StackOverflow answer that is in relation to setting the logger level programatically (Why are the Level.FINE logging messages not showing?):
This is not recommended, for it would result in overriding the global configuration. Using this throughout your code base will result in a possibly unmanageable logger configuration.
On that note, I think Andy Thomas has a goodish answer related to not doing it non-programatically.
Programatically setting the Level
That being said, I want to go into a bit more detail about doing it programatically, because I think it has its uses.
Imagine a scenario where you are writing something with a command line interface and you have an option to specify the verbosity of your execution, or even where it goes to (as in dynamic log files). I may be mistaken, but you would probably not want to do this statically in a .conf file. Especially so if you don't want to make your userbase responsible for setting these things (for whatever arbitrary reason) in the config file. This comes at the expense of the above quote, however. Here is an example of how you can do it programatically, keeping all of the existing handlers to whatever level they are at already, and only FileHandler's assume the new level:
public static void setDebugLevel(Level newLvl) {
Logger rootLogger = LogManager.getLogManager().getLogger("");
Handler[] handlers = rootLogger.getHandlers();
rootLogger.setLevel(newLvl);
for (Handler h : handlers) {
if(h instanceof FileHandler)
h.setLevel(newLvl);
}
}
I wanted to expand on this, over the accepted answer for one reason in particular. When doing it programatically, you just want to make sure that you set the level for the logger and the handler(s). The way it works, is it will check to see if the request is too low for the logger, and if it is it will discard it. Then the handler(s) have the same check, so you will want to make sure both the loggers and handlers are set to the level you want it.
One-liner Java 8 approach to morja's answer:
Arrays.stream(LogManager.getLogManager().getLogger("").getHandlers()).forEach(h -> h.setLevel(Level.INFO));
JUL(java.util.logging) is java default logger within jvm.
Java Logging Technology--see Overview
So you may find that there is a default config file already in C:\Program Files\Java\jre1.8.0_221\lib\logging.properties
I recommend that you create a new config file under your project to override the default setting, do as following:
config file name, logging.properties, all log level can be found in Class Level
handlers= java.util.logging.ConsoleHandler
.level= INFO
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format= [%1$tF %1$tT] [%4$-7s] %5$s %n
# your specific logger level
com.xyz.foo.level = SEVERE
Then add jvm option to enable the config
java -Djava.util.logging.config.file="logging.properties" -Duser.country=CN -Duser.language=en
user.country and user.language may have an effect on log message localization

How to enable FINE logging for a single java class

I'm using java.util.logging.Logger logging in my program. How do I enable FINE logging for a single class, while setting it to WARNING for every other class?
I'd prefer to do this programatically in my main() method rather than needing to set up additional properties files.
I know the OP has asked to do this programatically but here's an example of how to do it in the properties file too.
Caveat: I thought it was worthy of inclusion as the header doesn't indicate programatically and many developers will want to manage it through the logging.properties. Also there isn't really a lot on-line about this, it can be confusing and is slightly different to, say log4j
The root logging level is indicated by the .level config. This dictates which events are by default to be captured and "distributed for" logging. The root logging level is the level used by the "root logger" in the logging hierarchy. See this onjava article for more info on the logging hierarchy.
Below, the root log level is set to WARNING so will ordinarily capture only WARNING events. This is inherited by all child loggers in the hierarchy, unless you configure otherwise (later):
.level=WARNING
This root-logging level only indicates what is captured, not what is "distributed". How a captured event (message) is distributed is down to the handlers associated with the logger. For instance, a ConsoleHandler will output the event to the console. For instance:
java.util.logging.ConsoleHandler.level = WARNING
This ConsoleHandler.level indicates the level for which this handler should distribute - or print - the message. So, if a FINE message is received with the above config then this handler will not print it. It will print any messages with a WARNING log level or above though.
Setting to ALL will ensure that the ConsoleHandler will print all messages to the console (an we also need to configure the root level to ensure all are captured):
.level=ALL
java.util.logging.ConsoleHandler.level = ALL
However, this would create a lot of noise which we also don't want. So, to reduce the FINE-level events to those classes we're interested in, we change the logging level of those specific loggers only:
com.level = WARNING
com.mypackage.MyClass1.level = FINE
com.mypackage.MyClass2.level = FINE
com.mypackage.mysubpackage.MyClass3.level = FINE
Note that in the above, I've explicitly set the level for the "com" logger to WARNING.
Logger log = Logger.getLogger(this.getClass().getName()).setLevel(Level.FINE);
If you do not want to have a logger defined for every single class in question but rather want to share loggers between classes, you can alternatively implement your own java.util.logging.Handler that has its own way of filtering for class names using the information provided by LogRecord.getSourceClassName().
I believe that you can set your log level for your Handler and your specific class Logger to FINE, while keeping all the other Loggers for the rest of your code base to WARNING should do the trick. Both the Logger and the Handler need to pass the level filter for a message to be logged.
Well, here's a method I added to my mail class that's actually working. I would still welcome any improvements from others.
private static void setupLogging() {
// To enable FINE logging in a single class, apparently this bewildering
// maze of statements is required.
Logger.getLogger("").setLevel(Level.FINE);
for (Handler handler : Logger.getLogger("").getHandlers()) {
handler.setLevel(Level.FINE);
}
MyOtherClass.logger.setLevel(Level.FINE);
Logger.getLogger("").setLevel(Level.WARNING);
}

Categories

Resources