log4j2 extending Logger class - java

I am trying to migrate from Log4j 1.7 to Log4j2.4
In 1.7, I as creating AppLogger class by extending org.apache.log4j.Logger and using extending debug/error/fatal Methods
e.g.,
public void error(Object message) {
Object error = message;
if (message instanceof Exception) {
Exception e = (Exception) message;
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
error = sw.toString();
}
super.error(error);
}
But in Log4j 2.x, I am not able to extend the class org.apache.logging.log4j.Logger;
What is the Best way to achieve this?

I can understand why you might have wanted to do this with Log4j 1.x, but I cannot figure out why you would want to do this with log4j 2. The best place to do this is in a Layout, but most already do this.

As Ralph pointed out, Log4j2 has a very rich set of functionality, and if you go over the documentation, you often find that you can achieve your goals using the built-in layouts, lookups, filters and other features.
Writing a custom lookup only takes a few lines of code and gives you a powerful hook into the logging subsystem which you can use for many purposes.
If you really want to extend Logger for whatever reason, be aware that some layouts require Log4j to walk the stacktrace to report which class and line in your code from the logger was called from. This mechanism is a bit complex and in general I would recommend you create a Logger wrapper instead of extending it. (Prefer delegation to subclassing.)
Log4j has a tool to make this easy for you: the Custom Logger Generator tool documented in the Custom Log Levels page. Try this and see if it gives you what you need.

Related

How to enforce SLF4J usage in Java code with ArchUnit

I wanna use ArchUnit to enforce usage only SLF4J logging framework and avoid simple System.out calls. Also I would like to avoid any other logging frameworks for using. How can I implement the such check?
Currently I write this test
class EnforceSlf4JLoggingTest {
private final JavaClasses importedClasses = new ClassFileImporter()
.withImportOption(new ImportOption.DoNotIncludeTests())
.importPackages("... my packages ...");
#Test
public void classesShouldNotUseJavaUtilLogging() {
NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING.check(importedClasses);
}
#Test
public void classesShouldNotUseSystemOutLogging() {
noClasses()
.should()
.dependOnClassesThat()
.belongToAnyOf(java.lang.System.class)
.because("use SLF4J instead")
.check(importedClasses);
}
}
but it doesn't actually enforce SLF4J usage but only restrict java.unit.logging usage and prevent of having dependency to java.lang.System class (where actually System.out constant is located).
Whether is more elegant solution of my case?
There is no a simple way to avoid usage any other loggig systems beside of SLF4J but it's possible to use this ArchUnit check template
ArchRuleDefinition.noClasses()
.should().dependOnClassesThat()
.resideInAnyPackage(
"java.util.logging..",
"org.apache.logging.."
).as("Please only depend on SLF4J as a logging framework");
In the section resideInAnyPackage should be specified packages of different logging systems have to be avoided.
Also, rule
com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS
has to be used to check absents any plain System.out, System.err and printStackTrace calls in code.

How to set logging level in java?

At the moment this is how I'm handling logging:
private static final Logger log = Logger.getLogger( ClassName.class.getName() );
log.log( Level.FINE, "processing {0} entries in loop", list.size() );
But how do i set the log level? I dont want to do this in each class??
each log have a level :
log.debug(processing {0} entries in loop", list.size() ); or /log.info ...
and in your confuguration file (log4j.properties) you have a level to display in file
if in lof4j.properties you have level=WARn then your log.debug and log.info are not display (for production environnement)
You can use annotation with SLF4j and lombok to reduce code
#Slf4j
public class Test{
public Test(){
log.info("constructor");
}
}
Since I see you are using 'Level.FINE' I assume you are using java.util.logging.
The LogManager class level documentation describes this in detail. You can also use JConsole to modify the logging level.
Depends on the framework you are using.
If you are using Log4j framework you can specify the logging levels for specific packages or classes. This can be done either through a properties file, xml configuration, or through code.
Take a look at the documentation, http://logging.apache.org/log4j/2.x/manual/configuration.html, for specific examples.

How to override java.util.logging.Logger to my private logger?

I have code fetched from jar that uses java.util.logging.Logger.
Jar contains about 1000 logger usages and each class start from:
private static final Logger LOG = Logger.getLogger(SomeClass.class.getName());
I want to handle all logs there, means, to point them to my Logger usage and not to java.util.logging.Logger.
Therefore I wrote my own logger.
So instead:
LOG.log(Level.SEVERE, "Error sleeping", e);
I can write:
MyLogger.toLog(TLogLevel.WFS_ERROR, "Monkey", "Error sleeping", e );
The problem is I need run over all java files and replace with mine.
Messy way, hmm
Does anyone know how can by easy way to convert java.util.logging.Logger to com.boo.MyLogger?
Thanks,
The SLF4J project has a jul-to-slf4j bridge that can be used to redirect java.util.logging.Logger calls to SLF4J. You could use that (by making your MyLogger implement the interface defined by SLF4J).
Note that, however, unlike all other logging libraries, j.u.l. is hard-wired into the Java class libraries and cannot be bridged without a performance penalty.
Also, I don't know what you are doing with MyLogger, but usually there is no need to write your own. There are plenty of logging implementations to choose from, and they can be configured in many different ways. And even if you do have to write your own Logger implementation, you should use an existing interface (such as SLF4J which seems to most popular these days).
Take a look at SLF4J:
The Simple Logging Facade for Java or (SLF4J) serves as a simple
facade or abstraction for various logging frameworks, e.g.
java.util.logging, log4j and logback, allowing the end user to plug in
the desired logging framework at deployment time.
Using that you could then also use logback (same author) to log to a common logging framework using the various bridges already available. Or, write your own, but either way you would not have to worry about replacing all that code...
Oracle's Java 7 Logger is configurable, its implementation is simply:
public static Logger getLogger(String name) {
// This method is intentionally not a wrapper around a call
// to getLogger(name, resourceBundleName). If it were then
// this sequence:
//
// getLogger("Foo", "resourceBundleForFoo");
// getLogger("Foo");
//
// would throw an IllegalArgumentException in the second call
// because the wrapper would result in an attempt to replace
// the existing "resourceBundleForFoo" with null.
LogManager manager = LogManager.getLogManager();
return manager.demandLogger(name);
}
So you can also via code set a logging level; besides declarative.
LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.INFO);
Lars Vogel has a nice page, also with its own Logger class.
All put together is quite workable, but maybe sometimes somewhat hard to understand.

Redirect xsl:message to log4j

I am using SaxonJ to perform XSLT transformation from Java.
My XSLT files have to output any debug/info as output since my transformation logic is complex.
Is there a way in which I can redirect to log4j so that I can store have the transformation information in a log file?
My system already uses log4j to log output to file. I want to append the transformation debug messages as well.
Any ideas?
I am pretty sure that you have already found out how to perform this. But I hope that my answer will be useful for other people.
All you need to do is implementing MessageListener interface: http://www.saxonica.com/documentation/javadoc/net/sf/saxon/s9api/MessageListener.html
public class MessageListenerImpl implements MessageListener {
private static Logger logger = Logger.getLogger(MessageListenerImpl.class);
public void message(XdmNode content, boolean terminate, SourceLocator locator) {
if (terminate) {
logger.error(content.getStringValue());
System.exit(1);
} else {
logger.warn(content.getStringValue());
}
}
}
You can configure your own Message emitter. There are various interfaces to do this, see for example
http://www.saxonica.com/documentation/javadoc/net/sf/saxon/lib/FeatureKeys.html#MESSAGE_EMITTER_CLASS
or there's a simpler interface in the s9api XsltTransformer class.

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

Categories

Resources