I use a property file for java.util.logging and want to log all classes under package:
aaa.bbb.ccc.*
the normal way (i.e. info, fine, finer) but class
aaa.bbb.ccc.ddd.MyClass
in its own logging file "My Class.log" with level finer.
The config should be done only via a properties file. How would this look like?
I tried various ways (e.g. different handlers) but not any succeeded: It never worked that both log files are written to.
To make the problem more concrete - the config I tried:
handler.performance.class=com.logging.handler.FileHandler
handler.performance.file=${LOGGING_ROOT}/performance.log
handler.performance.level=FINE
handler.fine.class=com.logging.handler.FileHandler
handler.fine.file=${LOGGING_ROOT}/finer.log
handler.fine.level=FINE
handler.async.class=com.logging.handler.AsyncBufferHandler
handler.async.level=ALL
handler.async.targets=fine
handler.asyncperf.class=com.logging.handler.AsyncBufferHandler
handler.asyncperf.level=ALL
handler.asyncperf.targets=performance
com.myapp.handlers=async,console
com.myapp.useParentHandlers=false
com.myapp.common.logging.handlers=asyncperf
com.myapp.common.logging.useParentHandlers=false
The class I want to log to this separate performance log is located beneath com.myapp.common.logging...
Found the solution - it was a wrong initialization:
The logger should be initialized with:
Logger.getLogger(MyClass.class.getName())
Then the config:
com.myapp.common.logging.MyClass.handlers=asyncperf
com.myapp.common.logging.MyClass.useParentHandlers=false
logs all logging messages of this class in the specified separate file as desired!
Define two File appenders for the two target files
Define one root logger to use the first appender
Define a second logger for the special class, to use the other appender
set additivity of the logger to false in order to make any message go to one but not both files
I think that you have problem because you can't configure 2 default FileHandlers, only one of them. So try to implement your personal subclass of FileHandler and configure it as a separate handler.
I don't remember if we can configure logger for separate class or only for package, so try also configure handlers to package of MyClass.
Related
I am migrating a large codebase from log4j to log4j2. Log4j has a method to set the SyslogHost.
syslog.setSyslogHost(syslogDomainName);
I am looking for an equivalent in log4j2.
For completeness, the original log4J code looked like this:
SyslogAppender syslog = (SyslogAppender)Logger.getLogger("available-loggers").getAppender("syslog");
syslog.setSyslogHost(syslogDomainName);
The new code is looking to be a bit more complicated:
org.apache.logging.log4j.Logger logger
= org.apache.logging.log4j.LogManager.getLogger("available-loggers");
org.apache.logging.log4j.core.Logger coreLogger
= (org.apache.logging.log4j.core.Logger)logger;
org.apache.logging.log4j.core.LoggerContext context
= (org.apache.logging.log4j.core.LoggerContext)coreLogger.getContext();
org.apache.logging.log4j.core.config.Configuration configuration = (Configuration)context.getConfiguration();
Appender appender = configuration.getAppender("syslog");
SyslogAppender syslog = (SyslogAppender) appender;
...
plus the replacement for
syslog.setSyslogHost(syslogDomainName);
Edit to add - I am wondering if I can use the AppenderBuilder to create a syslog appender with the correct hostname.
syslog.newSyslogAppenderBuilder().withHost(syslogDomainName);
EDITED -
I am still having trouble making this work.
The log4J2.xml socket configuration below works for basic logging and is what I'm using:
<Socket name="syslog" port="514" host="${sys:log4j.syslog.host}" protocol="UDP" ReconnectionDelay="200">
<JsonLayout2/>
</Socket>
We have a log collector in AWS that can go away at any time, then scale back up with a new IP address. I need to figure out how to get it to switch to that to that new IP.
Log4J1 was pretty simple to do with the syslog.setSyslogHost during runtime. Any ideas on what I am missing in Log4J2?
The first question I would ask is "Why do you want to do this"? If it is because you need to determine the host name dynamically I would point you at Log4j's Lookup mechanism. If you are wanting to log events in RFC 3164 (BSD) format, the SyslogAppender is normally configured as
<Syslog name="bsd" host="targetHost" port="514" protocol="TCP"/>
If you want to set targetHost dynamically then do something like:
<Syslog name="bsd" host="${sys:syslogHost}" port="514" protocol="TCP"/>
which would get the hostname from a system property named "syslogHost". See Lookups for more lookup options or you can write your own custom Lookup.
But if, for some reason, you really need to programatically configure the Appender then there are multiple ways to do it which are described on the Log4j web site. Which you choose depends on whether you are programmatically creating the configuration or whether you want to modify and existing configuration.
To programmatically create a configuration Log4j recommends using a ConfigurationBuilder. This follows Log4j's normal mechanism of creating a configuration in two passes. In the first pass whatever the configuration dialect is (including the ConfigurationBuilder) is converted into a node tree. In the second pass the node tree is converted into the actual configuration. The second pass is generally common to all configurations built this way. So when you look at the ConfigurationBuilder API you won't be building specific classes. Instead you will be creating configuration components, which will later cause the corresponding classes to be created.
Modifying an Appender of an existing configuration cannot be done in a thread-safe manner so it is not allowed. Instead, you can create a new Appender with the same name and start it, remove the existing appender from the Configuration, add the new Appender, modify all the LoggerConfig objects to reference the new Appender instead of the old one, then stop the old Appender. You can find an example of this on Log4j's custom configuration page.
I should also note that Log4j's SocketAppender, on which the SyslogAppender is built, accepts a DNS name that returns multiple ip addresses. If the connection fails the SocketAppender will try to reconnect at any one of the ip addresses.
I want to change the level of logging for all (about) of my web application controller classes dynamically. Each class that does logging contains this code:
private static final Logger logger = LoggerFactory
.getLogger(HomeController.class);
I learned that slf4j doesn't offer the functionality of setting a log level, so that one has to use the underlying log4j:
public static void setLogLevel(String level) {
org.apache.log4j.Logger logger4j = org.apache.log4j.Logger.getRootLogger();
logger4j.setLevel(org.apache.log4j.Level.toLevel(level.toUpperCase()));
logger.info("Sample Info After setLevel");
logger.debug("Sample Debug After setLevel");
logger.error("Sample Error After setLevel");
logger.trace("Sample Trace After setLevel");
logger.warn("Sample Warn After setLevel");
}
My idea is to have a controller method which changes the logLevel and maybe stores it in the database. My question is: How I can solve this elegantly without copy and pasting everything in 40+ files and modifying every controller method? Also note that the logger code is static, while my database access is not static.
How I can solve this elegantly without copy and pasting everything in 40+ files and modifying every controller method?
You use a single controller method, taking the logger name and desired log level as a query parameters, and call the string version of LogManager.getLogger(String name).
Also saving to the database and re-loading on restart is of course extra work to be done by you.
Alternatively, the controller can update the Log4j configuration file used at startup, and force Log4j to reload it whenever it's changed, e.g. by calling PropertyConfigurator.configure(String configFilename).
You can use Slf4j and externalize your application.properties file . You can configure the logging level in this file as per your needs.
logging.level.com.test=DEBUG
logging.level.org=INFO
I am not sure why you need to store this logging level in the Database.
In Log4j 1.2, you could simply take a logger and add an appender at runtime. This was handy for test purposes for instance. We used to create a mock appender and add it to the logger. Subsequently we could make different assertions.
What is the equivalent thing with log4j2?
This example for instance shows how other people were adding test appenders.
The log4j2 page shows a few examples on how to add appenders. However, they add appenders to the overall context. Which seems different from adding appenders for one specific logger.
Another observation is that if you use org.apache.logging.log4j.core.Logger as opposed to org.apache.logging.log4j.Logger, you can still add appenders. However most people use org.apache.logging.log4j.Logger. And in fact, LogManager returns an instance of org.apache.logging.log4j.Logger. So, I am not sure how to connect these two classes and whether they should be connected at all.
Another observation is that if I call
LogManager.getContext().getConfiguration().getLoggers()
I can get a ist of all LoggerConfig objects in the context. I subsequently add appenders to any LoggerConfig object. The question however is, how do I get the LoggerConfig related to an instance of org.apache.logging.log4j.Logger?
This is confusing me.
org.apache.logging.log4j.Logger is an interface located in the log4j-api module. org.apache.logging.log4j.core.Logger is a concrete class (implementing the above interface) which lives in the log4j-core module.
LogManager (also in the log4j-api module) returns something that implements the org.apache.logging.log4j.Logger interface, and if the log4j-core module is in the classpath this will be an instance of org.apache.logging.log4j.core.Logger.
So you can cast the returned Logger to org.apache.logging.log4j.core.Logger and add the Appender.
Each core Logger has a LoggerConfig (1-to-1 relationship). You can find the LoggerConfig that corresponds to the desired Logger because they have the same name.
By the below post i can able to configure log4j to log in N different file.
Creating multiple log files of different content with log4j
log4j: Log output of a specific class to a specific appender
But i question is FOO.java should able to log in 2 different files. Normal debug/infos in general logger and some statistics in different logger.
I use slf4j and log4j.. I can able to change the logging framework if needed.
Normally, loggers are named after the class, but you don't have to do that. You can name the logger something totally different, or use the class name with some prefix or suffix, e.g. for class org.example.Foo:
org.example.Foo <-- Standard logger name
org.example.Foo.stats
stats.org.example.Foo
Foo.stats
stats.Foo
stats.Bar
Using a prefix will allow you to redirect statistics from all sources (classes) to a separate file, in one config entry.
You decide what is appropriate for you.
Im using Log4j and i have the following problem: is there a way to add one more logger from your code ? (not root logger).
In my config file, i set up the following two loggers :
log4j.rootCategory=INFO, ALogFile
log4j.logger.BLog=INFO,BLog
I would like to remove second line from config file and add BLog from code. Is that possible?
The equivalent code should be:
Logger logger = Logger.getLogger("BLog");
logger.setLevel(Level.INFO);
logger.addAppender(Logger.getRootLogger().getAppender("BLog")); // see notes below
I.e. you obtain a Logger object for the name BLog, you then set the level to INFO and attach the appender you've presumable defined elsewhere in your config file, called BLog.
Note: based on the answers from this related question, you may need to attach the appender to a logger in order to be able to reference it, as my code does above. Or just define the appender in your code.