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.
Related
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.
}
QuickFix/J provides functionality to store its logs in database.
Is it possible to append another column (business ID) to one of its table in such a way that it does not cause problems in QuickFix/J's internal message logging?
If it is possible kindly mention the procedure to do it too.
The solution is to create your own Logger and LoggerFactory similar to the ones provided by QuickFix/J.
You can create a Logger by implementing the "quickfix.Log" interface, and a LoggerFactory by implementing the "quickfix.LogFactory" interface.
The easiest approach would be to use the private AbstractLog from QuickFix/J.
Creating the Log class:
Copy the AbstractLog class as it is from QuickFix/J's source and include it in your project.
Create a class which extends the AbstractLog class and implement all the abstract methods.
Create member variables for any extra field you want to append to the logs (e.g. business ID), and provide a constructor which takes is
as an argument and sets its value.
The "logIncoming" and "logOutgoing" methods take a String parameter. This is the data you want to log. At this point you can
append your own fields (added in point 3) to the logs. You can format
the log as you wish and you are free to use any method of output, i.e.
Console, database etc. as you will have to implement it yourself.
Creating the LoggerFactory:
Create a LoggerFactory that implements the quickfix.LogFactory interface.
In the "create" method, create and return the instance of the Logger you created before using the constructor you require.
The values that you need to be passed to the constructor can be kept as member variables of the LoggerFactory and set in
LoggerFactory's constructor.
You have a custom Logger now and can use it as QuickFix/J's own loggers are used, and QuickFix/J will automatically log using your logger.
ApplicationAdapter application = new FixInitiator();
SessionSettings settings = new SessionSettings("./config/initiator.cfg");
CustomLogFactory customLogFactory = new CustomLogFactory(settings, myCustomID);
DefaultMessageFactory messageFactory = new DefaultMessageFactory();
FileStoreFactory fileStoreFactory = new FileStoreFactory(settings);
socketInitiator = new SocketInitiator(application, fileStoreFactory, settings, customLogFactory, messageFactory);
socketInitiator.start();
Taking a look at QuickFix/J's own Logger and LoggerFactory implementations for help would be a good idea.
e.g. The Logger that Logs on Console: ScreenLog, ScreenLogFactory
QuickFix/J source:
https://github.com/quickfix-j/quickfixj
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
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);
}
We have several jobs that run concurrently that have to use the same config info for log4j. They are all dumping the logs into one file using the same appender. Is there a way to have each job dynamically name its log file so they stay seperate?
Thanks
Tom
Can you pass a Java system property for each job? If so, you can parameterize like this:
java -Dmy_var=somevalue my.job.Classname
And then in your log4j.properties:
log4j.appender.A.File=${my_var}/A.log
You could populate the Java system property with a value from the host's environment (for example) that would uniquely identify the instance of the job.
If the job names are known ahead of time, you could include the job name when you do the getLogger() call. You then can bind different appenders to different loggers, with separate file names (or other destinations).
If you cannot know the job name ahead of time, you could configure the logger at runtime instead of using a configuration file:
FileAppender appender = new FileAppender();
appender.setFileName(...);
appender.setLayout(...);
Logger logger = Logger.getLogger("com.company.job."+jobName);
logger.addAppender(appender);
We have something similar implemented in our system. We store the specific loggers in a HashMap and initialize appenders for each of them as needed.
Here's an example:
public class JobLogger {
private static Hashtable<String, Logger> m_loggers = new Hashtable<String, Logger>();
private static String m_filename = "..."; // Root log directory
public static synchronized void logMessage(String jobName, String message)
{
Logger l = getJobLogger(jobName);
l.info(message);
}
public static synchronized void logException(String jobName, Exception e)
{
Logger l = getJobLogger(partner);
l.info(e.getMessage(), e);
}
private static synchronized Logger getJobLogger(String jobName)
{
Logger logger = m_loggers.get(jobName);
if (logger == null) {
Layout layout = new PatternLayout("...");
logger = Logger.getLogger(jobName);
m_loggers.put(jobName, logger);
logger.setLevel(Level.INFO);
try {
File file = new File(m_filename);
file.mkdirs();
file = new File(m_filename + jobName + ".log");
FileAppender appender = new FileAppender(layout, file.getAbsolutePath(), false);
logger.removeAllAppenders();
logger.addAppender(appender);
}
catch (Exception e)
{ ... }
}
return logger;
}
}
Then to use this in your job you just have to use a one line entry like this:
JobLogger.logMessage(jobName, logMessage);
This will create one log file for each job name and drop it in its own file with that job name in whichever directory you specify.
You can fiddle with other types of appenders and such, as written it will continue appending until the JVM is restarted which may not work if you run the same job on a server that is always up, but this gives the general idea of how it can work.
You can have each job set NDC or MDC and then write an appender that varies the name based on the NDC or MDC value. Creating a new appender isn't too hard. There may also be a appender that will fit the bill in the log4j sandbox. Start looking in http://svn.apache.org/viewvc/logging/log4j/trunk/contribs/
You could write your own appender that makes up its own filename, perhaps using the [File.createTempFile](http://java.sun.com/j2se/1.5.0/docs/api/java/io/File.html#createTempFile(java.lang.String,%20java.lang.String)) method. If the FileAppender class was written correctly, you should be able to extend it—or RollingFileAppender—and override the getFile method to return one that you choose based on whatever new properties you would like to add.
Building on shadit's answer. If each job can be identified by which class' main method was started you can use the system property sun.java.command that contais the full name of the class started. For instance like this:
log4j.appender.LOGFILE.File=${sun.java.command}.log
I use it together with a TimestampFileAppender like this:
log4j.appender.LOGFILE=TimestampFileAppender
log4j.appender.LOGFILE.TimestampPattern=yyyy_MM_dd__HH_mm
log4j.appender.LOGFILE.File=${sun.java.command}_{timestamp}.log
This way when I'm developing in Eclipse I get a new log file for each new process that I run, identified by the classname of the class with the main method and the time it was started.
Tom you coud specify and appenders for each job. Let's that you have 2 jobs corresponding to two different java packages com.tom.firstbatch and com.tom.secondbatch, you would have something like this in log4j.xml :
<category name="com.tom.firstbatch">
<appender-ref ref="FIRST_APPENDER"/>
</category>
<category name="com.tom.secondtbatch">
<appender-ref ref="SECOND_APPENDER"/>
</category>
You could programmatically configure log4j when you initialize the job.
You can also set the log4j.properties file at runtime via a system property. From the manual:
Set the resource string variable to the value of the log4j.configuration system property. The preferred way to specify the default initialization file is through the log4j.configuration system property. In case the system property log4j.configuration is not defined, then set the string variable resource to its default value "log4j.properties".
Assuming you're running the jobs from different java commands, this will enable them to use different log4j.properties files and different filenames for each one.
Without specific knowledge of how your jobs are run it's difficult to say!
you may implement following:
A ThreadLocal holder for the identity of your job.
Extend FileAppender, your FileAppender has to keep a Map holding a QuietWriter for every job identity. In method subAppend, you get the identity of your job from the ThreadLocal, you look up (or create) the QuietWriter and write to it...
I may send you some code by mail if you wish...
log4j.logger.com.foo.admin=,AdminFileAppender
log4j.logger.com.foo.report=,ReportFileAppender
It's another way to do this task.. here com.foo.admin is the full package name