Different logger based on caller - java

I need to re-factor the log files for a major project. Currently all logs goes to the same file. This file grows over 100MB a day. The problem is that there is a lot of scheduled code running every 5 minutes that fills up the log file. The main goal of the re-factor is that all logs that is created due to a schedule goes into separate files.
Each class in the project has the following:
final static Logger logger = LoggerFactory.getLogger(MyClass.class);
It is simple to only change it like this in classes that runs scheduled:
final static Logger logger = LoggerFactory.getLogger("scheduledLogger");
The problem is that some classes are called from a scheduled class and a non scheduled class and the logging must go to the appropriate log file.
It simply not possible to change every single function call in the entire project to send on the appropriate logger. The current solution which I have thought of is to create a object adapter like follows:
public class LoggerAdapter implements Logger {
private Logger defaultLogger;
private Logger scheduledLogger1 = LoggerFactory.getLogger("scheduledLogger1");
private Logger scheduledLogger2 = LoggerFactory.getLogger("scheduledLogger2");
//.. there are about 10 scheduled loggers
public LoggerWrapper(Class<?> Clazz) {
defaultLogger = LoggerFactory.getLogger(Clazz);
}
//Implement each function like so
public void debug(String s) {
Logger logger = findLoggerToUse();
logger.debug(s);
}
public void trace(String s) {
Logger logger = findLoggerToUse();
logger.trace(s);
}
//See if logging has been called from a scheduled class
private Logger findLoggerToUse() {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement stackTraceElement : stackTraceElements) {
String className = stackTraceElement.getClassName();
if (fastStringCompare(className,SCHEDULED_CLASS_NAME_1)) {
return scheduledLogger1;
}
if (fastStringCompare(className,SCHEDULED_CLASS_NAME_2)) {
return scheduledLogger2;
}
}
return defaultLogger;
}
Then in each class in the entire project I only need to change:
private Logger logger = new LoggerAdapter(MyClass.class);
The problem with this is that trace(String s) prints a trace to LoggerAdapter instead of the actual caller. I would like to know if there is a better way of solving the whole overall problem or if there is a small fix for the trace calls.

Instead of changing the code everywhere, write a custom appender. I suggest you try to switch to slf4j with logback or log4j-2 before since both make writing custom appenders much, much more simple. slf4j has drop-in replacements for the old log4j API, log4j-2 has documentation how to migrate.
Now you need to write an appender which is controlled by a thread-local variable. slf4j and the old log4j call this "MDC", log4j-2 calls it "Thread Context".
You set this variable when the scheduler code starts and clear it when it ends. Everything executed in the same thread will eventually go through the appender which can examine the variable and route the messages to the correct file.

Related

If wrapping Log4j how does one get it to print the calling location's line number

Log4j neatly extracts the line number for log prints most likely from using a Throwable to get the line number from by looking at it's StackTrace.
Alas Logger is not an Interface in Log4j so one can't just make another implementation of it with debug,info,error etc methods on it and can then switch implementations as needed for the platform.
so if one creates a wrapper ie:
class Parent {
static final Logger log = Logger.getLogger(Parent.class);
static void debugLog(String str) {
log.debug(str);
}
static void debugLog(String str, Throwable t) {
log.debug(str, t);
}
}
how can one have it put the line number of what is calling debugLog() into the log4j logger so it works as one would desire. Or is this simply a horrid design flaw in Log4j.
You have to calculate the location yourself and build a LocationInfo.
Then you have to create a LoggingEvent and call the callAppenders method.

Is there any way with one of the Java logging frameworks to restrict log records from being logged unless some security feature is disabled?

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);
}
}
}

Add a static prefix to everylogged message with slf4j

Is there a way to add a static (not context dynamic) prefix to all logged messages when using slf4j without altering the formatting of the underlying logging framework? To be clear, that means that using MDC is not an option because this would need to be reflected in the formatting configuration of the underlying framework.
Is there another option than creating a custom wrapper for the slf4j logger and using it wherever I would normally simply initialize a slf4j logger?
public class CustomLogger {
private final String prefix = "custom-prefix";
private final Logger logger;
public CustomLogger(Class clazz) {
logger = LoggerFactory.getLogger(clazz);
}
public void info(String info) {
logger.info (prefix + info);
}
// other methods...
}
If some context is needed: I am working in an environment where multiple plugins run on a core application. Within such a plugin I would like to add the plugin's name as a prefix to logged messages.
Wrap your calls in another method that does a String.format(...) on the passed in String. Your formatter will have your static string, and will place the logging string somewhere before, within, or after that, before passing it onto the actual logger method.

how to set LogManager.getLogManager().readConfiguration once for whole project using java util logger?

In my project for every class having log messages like
Logger.getLogger("LoggingExample.class").info("Logging an INFO-level message");
how to specify common LogManager.getLogManager().readConfiguration at
one place instead of writing configuration for every file.
For java util logger it is default logging.properties taking from
java home directory i don't want to maintain properties file in java home directory i
want to maintain it with in project folder how to do this in java util logger
You do not need to create a logger every time. As java.util.Logger is thread safe, it is feasible to declare it only once and use it every time. Below is an example:
private static final Logger myLog = Logger.getLogger("loggerName");
myLog .warn("Text");
Best practice is to use one logger per class. And if it does not involve more efforts then, I would advise to use log4j framework.
You'll want to hold a strong reference to a logger so you don't lose your configuration changes. Here is a modified example taken from the Java Logging Overview:
public class Nose {
// Obtain a suitable logger.
private static final String CLASS_NAME = Nose.class.getName();
private static final Logger logger = Logger.getLogger(CLASS_NAME);
public static void main(String argv[]) {
// Log a FINE tracing message
logger.fine("doing stuff");
try{
Wombat.sneeze();
} catch (Exception ex) {
// Log the exception
logger.log(Level.WARNING, "trouble sneezing", ex);
}
logger.fine("done");
}
}
If you want to use a custom configuration file you can specify the 'java.util.logging.config.file' system property to point to path of your properties file. This is explained in detail in the LogManager documentation.

Accessing a logger in a Java application

I have a question regarding logging in a Java application. I want to log the main user actions and possible errors with user friendly messages. Therefore, I have two appenders for the logger: one shows errors (level = error) in dialogs and the other writes the logs of the current user session into an html-file so that the user could send this file back if something goes wrong.
To avoid having the logger creation in every class (private Logger logger = …) I have a static reference of the configured logger in a class App which has also the methods for accessing the logger:
public class App {
private static Logger logger = Logger.getLogger("logger name");
…
public static void logError(String message, Throwable cause) {
…
}
public static void logInfo(String message) {
…
}
}
The logging is mainly done in the UI classes:
class UIWidget extends UIFrameworkWidget {
void aMethod() {
try {
someBusinessLogic();
} catch (Exception e) {
App.logError(“log message”, e);
}
}
}
Is this a good practice? (Note that the widgets are created by the framework.)
Thanks in advance for answers, comments, or hints on this
Better would be to use a single static Logger instance but allow each class to create a private instance which is initialized with the name of the class (and maybe other info). This private instance then uses the static instance to actually perform its logging but can be omitted if necessary and reduces references to external classes.
This is how slf4j does it, which is a logging framework you should consider using - however you could roll your own in the same manner.
Note, however I don't know how to get your error messages to be displayed within a dialog box - that may need to be explicitly added.
It seems you're just one step away of subclassing the JDK Logger. Having only one static instance prevents you from targeting specific classes at runtime. If you subclass the Logger, then you can still have has many loggers as logged classes and yet keep your appender's peculiarities.
You can also craft your own layout (and, in log4j at least, even add placeholders - geronimo has an example of this).

Categories

Resources