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.
Related
I need to have custom log levels(other than java.util.logging.Level), and custom functions in the java util logger to log messages of these levels.
Custom levels:
1. DEBUG
2. ERROR
3. FATAL
So I created XYZLevel extended from Level as follows:
public class OKILevel extends Level {
public OKILevel(String name, int value) {
super(name, value);
}
/* OKI Log Levels */
public static final Level FATAL = new OKILevel("FATAL",
Level.SEVERE.intValue() + 100);
public static final Level ERROR = new OKILevel("ERROR",
Level.SEVERE.intValue());
public static final Level DEBUG = new OKILevel("DEBUG",
Level.FINE.intValue());
}
Custom functions required:
.debug(), .severe() etc similar to .info in logger.
For logger I do:
static final Logger logger = Logger.getLogger(ABC.class.getName());
I am unable to figure out:
static final XYZLogger logger = XYZLogger.getLogger(ABC.class.getName());
extending the Logger doesn't help.
Also, for this application the logging.properties need to be passed in.
How do I use these new levels in .level of logging.properties?
If you are creating log wrapper then you should leverage some of the standard frameworks before inventing something new.
If you want your application to use a new logger implementation then you have to implement and install a custom LogManager.
Also, for this application the logging.properties need to be passed in. How do I use these new levels in .level of logging.properties?
All of the .level parsing performed via the
Level.parse method. Per the docs:
The argument string may consist of either a level name or an integer value.
For example:
"SEVERE"
"1000"
So you should be able to write:
#Level.SEVERE + 100 = FATAL
some.logger.name.level = 1100
That said there are some known issues with finding the correct level value by int in some releases of Java.
Another option if you just want the rendered output of a level to be different then you should have a look at Best way to override java.util.logging Level class to change the level strings.
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 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.
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.
I don't want to use something like this :
Logger.getLogger(MyClass.class).info("Info message");
each time I want to log something.
I've tried to implement a logging service that has methods like :
public void info(final Object aMessage, final Object aSender);
This allows me get Logger instance based on sender's class, but it hides the method and line of the log source class.
I know about alternatives like AOP or Slf4j. The first one is not exactly I want, the second one introduces something similar to the first line of code :
LoggerFactory.getLogger(HelloWorld.class).info("Info message");
So, my concern is not about hiding Log4j dependency, it's about unifying logging calls through the whole application, like this :
//loggingController instance was injected previously
loggingControler.info("Info message",this);
Is there any way to implement this ?
Ok, seems that there is at least one way to resolve the issue :
For example there are LoggingService and LoggingController. The first one works directly with Log4j, the second one is just a layer between service and the whole application.
public class LoggingService implements ILoggingService{
//other methods here.
#Override
public void warn(final Object aMessage, final Object aSender, final Throwable aThrowable) {
log(aMessage, aSender, Level.WARN, aThrowable);
}
private void log(final Object aMessage, final Object aSender, final Level aLevel, final Throwable aThrowable) {
final String className = getClassNameBy(aSender);
Logger.getLogger(className).log(className, aLevel, aMessage, aThrowable);
}
}
public class LoggingController implement ILoggingController{
//logging service is injected previously
#Override
public void warn(final Object aMessage, final Throwable aThrowable) {
loggingService.warn(aMessage, this, aThrowable);
}
}
So, in this case you allow the user to log something using :
loggingController.warn("A warning", null);
Using this way:
User knows nothing about underlying logging functionality
You always have the possibility to provide a dummy logger if you don't need it, or the environment doesn't allow it.
The logging functionality is unified across the whole application
The class name and code line are shown correctly in the log.
You cannot use one of the most useful features of the Log4j - filtering by package/class name.