In Java - Can I set log level pr thread? - java

I always wondered if it would be possible to programatically set the log level of java.util.logging on a pr thread level.
The usecase is, in a web server. If my code detects that a certain user has an abnormal level of errors, I would like to enable trace logging for that customer.
I was thinking of doing this with a servletfilter, and tie it to his session id. But as far as I can see, the log levels I can set on appenders are not thread affine. I.e. if I set the root level to FINE, it will affect all threads in the system, and the log fills up with the other 200 simultanious users traffic. Sure, I can then filter the log afterwards, but the performance hit will occur anyway.
Does anyone have experience with this?

I always wondered if it would be possible to programatically set the log level of java.util.logging on a pr thread level.
Create a log filter to capture the user context and set the level to ALL for the filtered object:
public class UserFilter implements Filter {
private final Level lvl;
private final Object userContext;
public UserFilter(Level originalLevel, Object userContext) {
this.lvl = originalLevel;
this.userContext = userContext;
}
#Override
public boolean isLoggable(LogRecord record) {
if (!belongsTo(record, userContext)) {
int levelValue = this.lvl.intValue();
if (record.getLevel().intValue() < levelValue
|| levelValue == Level.OFF.intValue()) {
return false;
}
}
return true;
}
private boolean belongsTo(LogRecord record) {
/**
* Insert logic here.
*/
//return ((Integer) this.userContext) == record.getThreadId();
return true;
}
}
For your use case you should be able to create a servlet filter which will:
Create a log filter which will identify a user and capture the current log level.
Install the log filter then change the level to FINE.

Well, you should always be able to call log(level, message); directly and get the actual level by using some thread local mapping.
You could also write some wrapper which handles this for you, i.e. with something like this:
public void debugExceptCritical(Logger l, String message) {
//critical would be your per-thread flag,
//just make sure to reset it when it is no longer used, e.g. in a servlet filter
l.log( critical ? ERROR : DEBUG, message );
}
Alternatively you could try and get the class where this is called (I did it somewhere but don't remember the details, so I'd have to look it up) and use this info to get the logger inside the method.
Looking up the caller:
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for( StackTraceElement elem : stackTrace ) {
//look for the debugExceptCritical method or maybe just the class it contains
//if found take the class name of the next element to look up the correct logger
}
An alternative to this, depending on which logging framework you actually use, might be to hook some per-thread filter/appender/whatever into it. In Log4J I'd either try to add my own appender which then checks the critical flag and changes the log event's level or always log critical actions and use a per-thread filter which rejects those log events when the critial flag is not set.

Related

Logging: Associate logs with object being created

I have Spring Boot project with a MainService that uses several helper methods and other "microservices" to create a FinalObject, that's eventually persisted using Hibernate/JPA. The methods and services may log several messages, and I want these to be associated with the object that was being created when the logged event occurred.
The problem is that the helper methods and microservices don't have access to the finalObject instance, so even though everything is logged, only caught exceptions get saved as a finalObject attribute - not warning messages or other logs:
class FinalObject {
private int value;
private int price;
private List<String> logs;
...
}
class MainService {
#Autowired ValueService valueService; // + other services
void createFinalObject() { // Main method
FinalObject o1 = new FinalObject();
try {
o1.setValue(valueService.getValue("some argument"));
}
catch (Exception e) {
log.error(e.toString()); // Logging using Log4j2
o1.addLog(e.toString()); // If there's an exception, I can easily log it to a o1 attribute.
}
o1.setPrice(calculatePrice(o1.getValue()));
...
}
int calculatePrice(int value) { // Helper method
if (value > getMarketPrice())
log.info("This is very valuable!"); // I need a way to associate this with o1!
...
return price;
}
}
// ValueService.java
int getValue(String arg) {
if (arg.matches("\\d$"))
log.warn("arg ends with a number"); // Must also be saved to o1!
...
return value;
}
Solution 1: Passing o1 around everywhere:
int calculatePrice(int value, FinalObject o1) {
if (value > getMarketPrice()) {
o1.addLog("This is very valuable!"); // Now I have access to o1 here
log.info("This is very valuable!");
}
...
Solution 2: Pass o1's logs attribute around as a modifiable list:
o1.setPrice(calculatePrice(o1.getValue(), o1.getLogs()));
...
int calculatePrice(int value, List<String> finalObjectLogs) {
if (value > getMarketPrice()) {
finalObjectLogs.add("This is very valuable!"); // Directly modify o1's logs attribute
log.info("This is very valuable!");
}
...
Solution 3: Add a log4j2 database appender
A more elegant solution may be do add a database appender to log4j2. A challenge with this is how I can relate these logs to o1. The FinalObject id only gets generated at the very end of createFinalObject(), when it is saved to the database, so I don't have an id when the log statements are executed.
Question:
How can I do this more elegantly than the ways I mentioned above?
Or:
If solution 3 seems like a good approach, how do I implement it?
I am curious. In your example the method calculate price doesn't seem to relate to any object so why would you want to include information about a particular object in it?
On the other hand, if you want to correlate it with other operations being performed in a single Request I would suggest you look at the RequestContext that Log4j-Audit describes. You don't have to use Log4j Audit to implement something like this. It simply leverages the Log4j ThreadContext and defines specific keys that are used. You then initialized the values in the ThreadContext and the beginning of the request and clear it at the end of the Request. You can add items to the ThreadContext as needed. Log4j can then be configured to include specific keys in every log event.
I should note that Spring Cloud Sleuth is doing something that I have been doing for years. To propagate the Request information from one service to the next they simply need to be converted to and from HTTP headers when calling a service and when the service starts. That is why the Log4j-Audit example RequestContext shows annotations to classify the attributes as ClientServer, Local, or Chained. It then provides the components needed to accomplish this.
You can use spring sleuth and logging filters to log the endpoints.
Check the Spring Sleuth Docs;
https://docs.spring.io/spring-cloud-sleuth/docs/2.2.4.RELEASE/reference/html/
You can create a filter do log every endpoint, or log one of them.
And with this loggers you can log the types that you want.

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

Custom Log Level

In my existing application "org.apache.log4j" API's have been used in java code.
Requirement :
I have to log some statement(say xyz) in log file in any case and should not dependent of log levels.For example : if my log level is error then also xyz should print, if my log level is debug then also xyz should print.
I cannot make log statement of xyz is debug because if i do this, other log statements apart from xyz will also start printing.
For this, I believe, I have to add some custom log level.Please help how to do it and how to set its level ordering so that in any case it should print.
Thanks in advance.
Best Regards
What you could do is create a different Logger for those statements (you are not restricted to use classes names when defining a logger)
// Standard logger
private static Logger log = Logger.getLogger(MyClass.class)
// XYZ logger
private static Logger logXYZ = Logger.getLogger("logs.xyz");
You can access the same logger from several class, you just have to pass the same label.
Then, in the configuration file, you can define a different log level for that category, and even output these logs in a different appender (different file, processing, etc.)
You could "hijack" the protected method Logger#forcedLog() to always print to the log.
You must place the hijacker class in the same package as Logger.
package org.apache.log4j;
/**
* #author maba, 2012-08-23
*/
public class LogOverride {
public static void print(Logger logger, String message) {
logger.forcedLog(logger.getName(), Priority.INFO, message, null);
}
}
And from your calling code
log.setLevel(Level.OFF); // Make sure logging is turned off
log.info("Normal logging"); // Will not be seen anywhere
LogOverride.print(log, "Overriding logger"); // Will still make it to your appender
This is what the log4j FAQ says about custom levels:
How do I add a custom level?
It is possible, but rarely appropriate. The request is commonly for a level named something like "audit" that doesn't obviously fit in the progression "trace", "debug", "info", "warn", "error" and "fatal". In that case, the request for a level is really a request for a mechanism to specify a different audience. The appropriate mechanism is to use a distinct logger name (or tree) for "audit" related messages.
So if you want to go with that suggestion then you should look at the answer from SJuan76.
If you do decide to go with the idea of creating a custom, you would need to create a subclass of Level to do this, because the Level constructor is protected.
/**
* Instantiate a Level object.
*/
protected Level(int level, String levelStr, int syslogEquivalent) {
super(level, levelStr, syslogEquivalent);
}
It looks like you should then chain to the Level constructor, passing it a suitable level value. Note that the larger the level number the higher the priority is. So for a Level that won't be blocked at any of the existing named levels, you want a value that is greater than Priority.FATAL_INT which is 50000.
(However, I'm not convinced that this is the right approach. For a start, you probably won't be able to refer to your custom level by name in a logging config file.)

Logging controller/service using Log4j ( Centralized logging )

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.

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