I have done some Googling around it but couldn't find any relevant information. log4j supports a bunch of log appenders, there's documentation all over the net about ConsoleAppender and FileAppender, but there are very little or no information about appenders such as NullAppender, JDBCAppender etc. I am particularly interested about NullAppender.
<appender name="???" class="org.apache.log4j.varia.NullAppender">
<appender name="???" class="org.apache.log4j.jdbc.JDBCAppender">
Does anyone have any specific info on these? especially on the NullAppender?
I started looking here.
For the NullAppender, there's not a lot of doc largely because there's nothing to configure: you can define one with <appender name="foo" class="org.apache.log4j.varia.NullAppender"/> and that's about it. From the Javadoc:
A NullAppender merely exists, it never outputs a message to any device.
There aren't a lot a different ways to do nothing. (It exists so that you can trash output without modifying too much of your config.)
For the JDBCAppender, the Javadoc is here: http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/jdbc/JDBCAppender.html
The param tags in the XML config correspond to setters in the Java class, but note the big red warning at the top of the Javadoc:
WARNING: This version of JDBCAppender is very likely to be completely replaced in the future. Moreoever, it does not log exceptions.
So maybe not the best class to be relying on, given that log4j v2.0 is currently in beta, and that a cursory look over the alpha release seems to indicate that it doesn't exist in v2.
A simple google for this class will get you
Nullappender
So essentially if you use the NullAppender your log messaged are written nowhere.
The NullAppender basically does nothing, like others have already pointed out. I just wanted to layout some details.
AppenderSkeleton implements Appender {
...
#Override
public synchronized void doAppend(LoggingEvent event) {
...
}
...
abstract protected void append(LoggingEvent event);
}
public class NullAppender extends AppenderSkeleton {
public static String s;
public String t;
...
#Override
public void doAppend(LoggingEvent event) {
if(layout != null) {
t = layout.format(event);
s = t;
}
}
#Override
public void append(LoggingEvent event) {
}
See also: Source-NullAppender & Source-AppenderSkeleton
What happens at each and every log.trace/debug/... call is
-> org.apache.log4j.Category.forcedLog(String fqcn, Priority level, Object message, Throwable t)
-> org.apache.log4j.Category.callAppenders(LoggingEvent event)
-> org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(LoggingEvent event) is called on each Appender within that Logger
-> org.apache.log4j.Appender.doAppend(LoggingEvent event)
which is NullPointers implementation of doAppend in this case.
As you see, the NullAppender takes away the synchronization, NullAppender.doAppend produces some overhead, but it is very little in most scenarios.
As for JDBCAppender it should not be used.
WARNING: This version of JDBCAppender
is very likely to be completely replaced in the future. Moreoever,
it does not log exceptions.
See: JDBC-Appender-Source Line 34-36
As an alternative you might want to check out clusterlog
Related
Although there are a few other similar questions, they all seem to misunderstand logging configuration with regards to wildcards / root loggers. I have a different issue.
I have a code structure as follows:
Service 1
com.some.package.service1
|-> subpackage1
|-> subpackage2
Service 2
com.some.package.service2
|-> subpackage1
|-> subpackage2
I would like to set the log levels of, say, all loggers of classes in subpackage1 to DEBUG, while setting the log levels of all loggers of classes in subpackage2 to WARN while leaving the rest at, say, INFO.
I had hoped that I would be able to simply configure something like the following:
logging:
level:
com.some.package: INFO
com.some.package.*.subpackage1: DEBUG
com.some.package.*.subpackage2: WARN
Unfortunately, this does not work at all - the configuration with a wildcard in it is silently ignored. Since I have numerous services, I don't want to clog up my configuration file with numerous package logging definitions which also have to be updated whenever I add a new service. Changing the code structure is unfortunately not an option for me.
Is it possible to do this using slf4j, either through plain configuration or programmatically (ideally using only the slf4j API, but I could live with an implementation-specific solution)?
If not, is there an alternative solution?
I managed to do this using, unfortunately, using the specific Logback Classic Logger implementation.
The end result is something like:
#Component
public class PackageScanningLoggingConfiguration {
// Fields
// ...
#PostConstruct
public void postConstruct() {
initLoggingLevels();
}
private void initLoggingLevels() {
String basePackage = getBasePackage();
List<String> candidatePackageNames = Arrays.stream(Package.getPackages())
.filter(pkg -> pkg.getName().startsWith(basePackage))
.filter(pkg -> {
String packageName = pkg.getName();
return packageName.contains(subpackage1Package)
|| packageName.contains(subpackage2Package);
})
.map(Package::getName)
.collect(Collectors.toList());
candidatePackageNames.forEach(this::setConfiguredLoggingLevel);
}
private void setConfiguredLoggingLevel(final String packageName) {
// ch.qos.logback.classic.Logger;
// org.slf4j.LoggerFactory;
val logger = (Logger)LoggerFactory.getLogger(packageName);
if (logger != null) {
if (packageName.endsWith(subpackage1Package)) {
logger.setLevel(Level.toLevel(subpackage1Level));
} else if (packageName.endsWith(subpackage2Package)) {
logger.setLevel(Level.toLevel(subpackage2Level));
}
}
}
// resolve com.some.package.* to specific instance
private String getBasePackage() {
// In my case, I have only two "types" of services, so this
// returns either com.some.package.service1 or com.some.package.service2
}
}
Unfortunately, due to Logback internals, it seems that for this solution to work, my root package (com.some.package) has to be set to TRACE and all others have to be lower than that - the other way around does not seem to work.
Due to this, I don't like this solution very much (it works, but it's kind of weird). I'll leave this question open for a bit in case anyone has an idea of a better way to do this.
logEvent.getContextData().size() == 0 and logEvent.getContextStack().size() == 0 but otherwise the attributes of the LogEvent are fine in:
public class MyAppender extends AbstractAppender {
.........
#override
public void append(LogEvent ev) {
ev.getDataContext().size(); // <=== how can this equals 0?
ev.getStackContext().size(); // <=== how can this equals 0?
....
}
}
I cannot figure-out why this is the case. Do I need to create an AbstractConverter? AbstractFilter? Is my log4j2.xml or maybe the plugin config wrong?
Based on our discussion in the comments, it looks like what you're actually after is the location information. In a custom appender, this can be obtained by walking the stack trace provided by LogEvent.getSource(). You should be aware that obtaining this information is expensive though (see the documentation).
Edit
As you've stated, location information can be very useful, so it's a shame that it's expensive to obtain. Unfortunately, there's nothing Log4J can do about that - it's down to java's architecture.
One cheaper method that's commonly used to obtain the class name at least, is to ensure that the Logger being logged to is named after the class in which it's used (see documentation here). Then, you can obtain the class name in an appender by calling LogEvent.getLoggerName(). Note, however, that if you're writing a general Appender implementation that may be reused across several projects, it would be bad practice to assume that this would always be the calling class's name. Instead, it should be interpreted as "the functional context that the logging call came from, as determined by the application".
Is there any way with one of the Java logging frameworks to restrict log records from being logged unless some security feature is disabled?
Use case:
Company X has Java software called SuperThing in package com.x.superthing
Java class com.x.superthing.SuperSecretThingy contains lots of important IP
Company X wants to be able to enable logging from SuperSecretThingy using one of the standard Java logging frameworks (java.util.logging, log4j, logback, etc.); if they have to do something special to make this work, that's ok.
When someone outside Company X wants to run SuperThing, they should not be able to enable logging from SuperSecretThingy, e.g. with -Dlog4j.configurationFile=... because some of the log messages contain sensitive information
Constraint: code in the class in question (SuperSecretThingy) must not have a compile-time dependency on anything special, so that the logging code is just the normal stuff.
class SuperSecretThingy
{
final static private Logger logger =
LoggerFactory.getLogger(SuperSecretThingy.class);
// this example uses SLF4J
...
public void foo()
{
logger.info("Entering foo");
do_stuff();
logger.info("Exiting foo");
}
}
There's a Filter feature in log4j:
In addition to the automatic log Level filtering that takes place as described in the previous section, Log4j provides Filters that can be applied before control is passed to any LoggerConfig, after control is passed to a LoggerConfig but before calling any Appenders, after control is passed to a LoggerConfig but before calling a specific Appender, and on each Appender.
Is there a way to do this programmatically? If so, then I can filter out log events from a specific class.
Perhaps you could make your own logger class that wraps the log methods so you test for whatever your condition is and then call the logger if the condition is what you want.
Something like:
class SuperSecretThingy {
final static private SecretLogger logger =
SecretLogger.getLogger(SuperSecretThingy.class);
public void foo(){
logger.debug("Entering foo");
do_stuff();
logger.debug("Exiting foo");
}
...
}
class SecretLogger {
private Log log;
public SecretLogger(Class c) {
log = LogFactory.getLog(c);
}
public void debug(String message) {
if (mySecretConditions()) {
log.debug(message);
}
}
}
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.
Before I reinvent the wheel - I want to be able to insert debugging traces in my code, such as say("We are here.");, without defining static void say() in every class. It needs to do System.out.println(s), and to be globally switched on or off (doSay(false)), and I'd also like it to be able to identify the class from which it's being invoked (as described here). For example:
MyClass: We are here.
Does Java already have such a tool?
Use SLF4j, not log4j (at least, not directly). They are both created by the same author, Ceki Gülcü, but SLF4J incorporates knowledge gained by seeing log4j in use, and looking at advances in other logging packages.
SLF4J is a common API for a number of different underlying logging systems, like log4j, the java.util.logging package, etc. It also has its own "native" implementation, logback.
One reason I like it better than log4j is its support for message templates. These keep your code simpler.
Also, it allows me to include logging in a library, but let the user of my library choose the logging implementation. Without something like this, a user might have to configure logging just for my library, and it wouldn't be unified with the rest of his application.
The most popular Java logging framework is Log4J which does this (and much more).
Look here for a list of other.
Yes. It's called a logging framework. Java has java.util.logging. But many prefer using Log4J.
You could use a logger like Apache's Log4J and do something like logger.trace("We are here");. When you want that off, set the log level higher (debug, warn, error) in your configuration and the trace logs will disappear.
Java has more advanced logging tools, like log4j or logback. There you should create a public static final Logger logger = Logger.getLogger(..) and use the logger to write debug/info/warn/error messages to wherever you like. They are highly configurable - what and where to log.
For the simpler case (if this is a toy project), you can simply define a class with the public static void log(..) method and use it from every class.
Why not static-declare a function in your Main.java, and use it allround?
public class Main {
private static boolean debug;
public static void setDebug(boolean d) { Main.debug = d; }
public static void say(String s) { if(Main.debug) System.out.println(s); }
}
Let me know if this fits your needs.
Edit: revised the code
I didn't know about static import! I combined ideas from #ninetwozero, #karl, and #erickson to create this:
package myPkg;
public class CLHUtilities {
private static boolean saying = false;
public static void tracing(boolean b) {
saying = b;
}
/*
* Technique taken from:
* http://stackoverflow.com/questions/282977/which-class-invoked-my-static-method
*/
public static void say(String s) {
if (saying) {
Throwable t = new Throwable();
StackTraceElement[] trace = t.getStackTrace();
String className = trace[1].getClassName();
String whoCalledMe = null;
try {
whoCalledMe = Class.forName(className).getSimpleName();
} catch (Exception e) {
}
System.out.println(whoCalledMe + ": " + s);
}
}
}
which can be used simply as:
import static myPkg.CLHUtilities.*;
:
tracing(true);
:
say("We are here.");
Which suits my needs perfectly.