Change global setting for Logger instances - java

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

Related

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

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.

Setting logger properties programatically to show hibernate queries

I'm using Hibernate and java.util.Logger class for logs in my project. I have a separate config file for both. I am able to switch between showing and not showing SQL queries by setting org.hibernate.SQL.level property to ALL in the log configuration file, but I can't figure out how to do it programatically (I want to handle this through run parameters but without having to use two seperate log configuration files).
So far I have tried setting this parameter in hibernate Configuration class, to no avail (properties are getting set, I double checked, but no queries show up).
Then I figured it must be handled by Logger class itself, but LogManager does not have any methods for setting a property. Browsing through the web guided me towards FileHandler class but I am able to set only the 'usual' log properties (like pattern, level, etc).
Does it mean I'm wrong in thinking I have to change the Logger class and it should in fact be set in hibernate's Configuration? If that's the case, why did it not work?
...I can't figure out how to do it programmatically (I want to handle this through run parameters but without having to use two separate log configuration files).
I would have assumed that the current situation solved the problem as log configurations are set using a run-time parameter. That said, the general approach is to get and store a strong reference to the logger and change the properties of that logger.
private static final Logger hardRef = Logger.getLogger("org.hibernate.SQL");
static {
if (traceSql()) {
hardRef.setLevel(Level.ALL);
}
}
private static boolean traceSql() {
return true; //#todo Add code.
}

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.)

How to configure the properties of a specific FileHandler

The Java logging configuration file lets me define the properties of a named logger, e.g.
name.heikoseeberger.heikotron.level = FINE
name.heikoseeberger.heikotron.handlers = java.util.logging.FileHandler
So far, so good. Now I would like to configure that particular FileHandler, e.g. with a specific output file. Unfortunately I only know how to configure the "global" FileHandler, which is already present in the configuration file:
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
I don't want to configure this one, but the instance which is associated with my custom Logger. I already tried the following, but without success:
name.heikoseeberger.heikotron.java.util.logging.FileHandler.pattern = %h/heikotron.log
name.heikoseeberger.heikotron.java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
Is it possible at all to set the properties of specific FileHandler instances? If yes, how should these be identified/named?
This is done by using the config option described in the top level class documentation of the LogManger. Create a public named class with a public constructor and invoke all of the java calls you need to make to configure your handler. Then in your logging properties direct the LogManager to load your class you created to configure your handler. Otherwise you can to subclass file handler which will create a custom namespace to configure.
I do not think it possible. If you review the source code for the FileHandler you will soon determine that it uses the string "java.util.logging.FileHandler.pattern" to determine the pattern of the file to use for logging purposes
private void configure() {
LogManager manager = LogManager.getLogManager();
String cname = getClass().getName();
pattern = manager.getStringProperty(cname + ".pattern", "%h/java%u.log");
limit = manager.getIntProperty(cname + ".limit", 0);
//...
}
As such, the configuration that you are putting in the file is not even been taken into account by the Handler.
It appears to me that handlers are unaware of the existence of any particular logger (i.e. name.heikoseeberger.heikotron), they just know how to publish a given LogRecord.
As far as I can see, the handlers of a particular logger are created by the LogManager, by reflectively invoking their default constructor, as such, when a given handler is being created, it is unaware of for which particular logger it has been requested, that is why all their properties are set through their own class names and not through the logger's name.
If I understand right you are trying to write in different log files using java.util.logging package. This can't be done out of the box without extending it.
If you can't switch to another logging framework like Logback, check answer to
java util logging.properties: How to log to two different files and see if it fits your needs.

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