My class is like this, basically I'm writing a servlet, and I want to change the log level for a specific user connected to my servlet and leave other log settings for other user unchanged, since the server will produce one thread to serve one client, I'm writing demo code use only threads
public Class A implements Runnable {
Logger myLogger = new Logger();
#Override
public void run() {
if (Thread.currentThread.getName()).equals("something") {
// some code that makes myLogger thread-local so I can change
// myLogger settings without affecting other threads
}
myLogger.debug("some debug information");
}
}
Any ideas how to do it?
Seems like this could be done in this way
public Class A implements Runnable {
private static final ThreadLocal<Logger> logger = new ThreadLocal<Logger>(){
//return your desired logger
}
#Override
public void run() {
//check condition and change logger if required
//check if that particular servlet and user also
if (Thread.currentThread.getName().equals("something") && user.getId() ==XX) {
ConsoleAppender a = (ConsoleAppender) Logger.getRootLogger().getAppender("stdout");
a.setLayout(new PatternLayout("%d{HH:mm:ss} %-5.5p %t %m%n"));
}
}
}
for more information:
When and how should I use a ThreadLocal variable?
java doc for Thread Local states that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable more.
Related
I want to pass objects between methods within a thread without using a method signature.
Example:
public class Controller {
#GET
public void requestController() {
// set user data in the controller currentThread
}
}
public class Service {
public void serviceMethod() {
// get user data in the service method from currentThread
}
}
as Controller and Service are in the same thread, I should be able to access the object in Service that was set in the Controller.
MDC does follow the same approach using MDC.put and MDC.get but I am not sure which pattern it uses.
Youre looking for a ThreadLocal. MDC uses that internally.
Usage
The usage is pretty simple. You need one ThreadLocal Instance that is accessable by all components that need access to it. In most cases its simply a public static final variable.
public class SomeClass {
// use whatever class you want here, String for example
public static final ThreadLocal<String> TL_MESSAGE = new ThreadLocal<>();
}
public class Controller {
#GET
public void requestController() {
SomeClass.TL_MESSAGE.set("hello world");
try {
// everything after set should be wrapped in this try-finally-block
service.serviceMethod();// this can be anywhere in the code, it doesnt have to called here directly. As long as the thread is the same and the method is called between set and remove
} finally {
SomeClass.TL_MESSAGE.remove();
}
}
}
public class Service {
public void serviceMethod() {
String message = SomeClass.TL_MESSAGE.get();
}
}
Pitfall / Memory leak possibility!
Ensure that you always remove the value you set.
For more information, see: ThreadLocal & Memory Leak
From this answer https://stackoverflow.com/a/25125159/4367326 I have routingAppender working but I want to set the ThreadContext for every thread in the program.
When I set
ThreadContext.put("logFileName", "TestLogFile");
it works for the main thread and logs as expected but not for any other threads in my application. How can I achieve this?
Every child thread will inherit fathers ThreadContext state if you set up system property isThreadContextMapInheritable to true. But this will not work for Executors so you need to manually copy data from one thread to another.
Update#2
You can do something like this:
public abstract class ThreadContextRunnable implements Runnable {
private final Map context = ThreadContext.getContext();
#Override
public final void run() {
if (context != null) {
ThreadContext.putAll(context);
}
try {
runWithContext();
} finally {
ThreadContext.clearAll();
}
}
protected abstract void runWithContext();
}
And then you only need to implement runWithContext method.
I want to add a custom handler to every logger for every class of my project. I have a logging.properties file, which is read at the very beginning using:
try (InputStream in = ReportingService.class.getResourceAsStream("logging.properties")) {
LogManager.getLogManager().readConfiguration(in);
} catch (IOException ex) {
Logger.getLogger(myClass.class.getName()).log(Level.SEVERE, null, ex);
}
The logging.properties file looks like this:
handlers=java.util.logging.ConsoleHandler,myPackage.AlertHandler
.level=SEVERE
java.util.logging.ConsoleHandler.level=SEVERE
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
myPackage.AlertHandler.level=SEVERE
myPackage.AlertHandler.formatter=java.util.logging.SimpleFormatter
And myPackage.AlertHandler.java looks like this:
package myPackage;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import javafx.scene.control.Alert;
public class AlertHandler extends Handler {
#Override
public void publish(LogRecord lr) {
Alert a = new Alert(Alert.AlertType.ERROR);
a.setTitle("Exception!");
a.setHeaderText("Exception was thrown, here is the StackTrace:");
a.setContentText(getFormatter().formatMessage(lr));
Platform.runLater(()->{a.showAndWait();});
}
#Override
public void flush() {
//no real handler is open, nothing to flush
}
#Override
public void close() throws SecurityException {
//no real handler is open, nothing to close
}
}
The logging file is read without issues, as no more INFO or WARNING messages are printed to the console, only SEVERE. But my custom handler is never called, as no Alert windows are ever opened. I also tried adding the handler to the global logger, in hopes of every other logger inheriting its handlers, but it doesn't work either:
Logger.getGlobal().addHandler(new AlertHandler());
Logger.getLogger("").addHandler(new AlertHandler());
Adding the handler to a specific logger works as intended: if an error is thrown anywhere in the class, an alert window is opened with the stacktrace.
Logger.getLogger("mySecondClass").addHandler(new AlertHandler());
But I want to add this handler to EVERY logger in any class!
But I want to add this handler to EVERY logger in any class!
If you want this handler to see all logger output then you just have to install one instance on the root logger. For example, Logger.getLogger("").addHandler(new AlertHander());
By default, child loggers are going to publish the log records to the parent handers.
The main problem is your code is going to always generate a NullPointerException because you never assigned a formatter to be used with the handler. So the call to getFormatter is going to return null and then fail.
public static void main(String[] args) {
Handler h = new Handler() {
#Override
public void publish(LogRecord record) {
}
#Override
public void flush() {
}
#Override
public void close() throws SecurityException {
}
};
System.out.println(h.getFormatter());
}
You also need to:
Add code to parse the level, formatter, and filter assigned by the log manager for your handler.
Choose sane defaults when no level, formatter, or filter values are defined.
Deal with non-JavaFX Application Threads creating and showing the dialog by using Platform.runLater.
Call isLoggable in your publish method to determine if your handler should publish or not.
Catch runtime exceptions inside your publish method and track them by calling Handler.reportError.
Make life easy and create unit tests to ensure your handler actually works before you try to use it in the wild.
I have a synchronized function that is initiated in a new thread from a loop in another function and there are many many calls to it. So I have:
foo(){
new SwingWorker() {
#Override
public void doInBackground() {
sync_foo();
}
}.execute();
} catch (IOException e) {
log.error("", e);
}
}
sync_foo is defined as:
private synchronized void sync_foo() {
}
I put some debug lines in sync_foo to check if it is working as I expected. so I had:
private synchronized void sync_foo() {
final Logger log = Logger.getLogger(getClass());
log.info("start");
...
log.info("finish");
}
The logger here is Log4J which I understand is indeed thread-safe. I noticed that in the logfiles I sometimes have two subsequent "start". So I added a further line near the end log.info("still alive") and kept putting it nearer and nearer to log.info("start"); to see if I ever stop getting double starts and always get still alive between the two, but I was still getting it and finally I resorted to putting it on the next line to log.info("start");:
private synchronized void sync_foo() {
final Logger log = Logger.getLogger(getClass());
log.info("start");
log.info("still alive");
...
log.info("finish");
}
but every now and then, I still get:
start
start
which i find very puzzling. It appears that the method is somehow interrupted but I can't understand how. I should add that sync_foo() is only called from foo() and I am not getting any exception or error of any kind.
So the question is:
In general, what are the possible causes of a method being interrupted by itself?
I understand that Swing worker might have its own ways of handling threads execution. The SwingWorker that i use above, is in fact overridden and it's defined as in here. Is there anything in this extension that makes it possible to miss the relevant exceptions?
Synchronization depends on sharing a lock, so if you see behavior where multiple threads are calling something that's synchronized concurrently, it means they're not using the same lock. It sounds like sync_foo is called on different objects. Using synchronized on the method means that the monitor on the object instance is acquired by a thread that wants to enter the method, so if the method is called on different objects there's no shared lock and nothing stopping a thread from entering the method on one object while another thread is executing the method on a different object.
You could make a class-level lock like this:
public static final Object LOCK = new Object();
and change your method to
private void sync_foo() {
synchronized(LOCK) {
final Logger log = Logger.getLogger(getClass());
log.info("start");
log.info("still alive");
...
log.info("finish");
}
}
so all the objects that sync_foo is called on will use the same lock regardless of what instance it's called on.
In my application I have a class which has a variable which gets updated everytime a new event comes in:
class Logger{
private String mVariable ="";
public void onEvent(Event e) {
//update mVariable here
}
public void log() {
//write mVariable to file
}
}
Now, I have another class which wants to trigger the Logger class to write the current value to a file.
class Trigger{
//this is another event, not the event for which Logger is waiting for
public void onEvent(Event e) {
mLogger.log();
}
}
How can I ensure that the Trigger class has exclusive access to the value of mVariable whenever it wants? i.e. When the Trigger class calls the log method the value of mVariable should not be overridden by the Logger class of course.
Make both methods synchronized then calling lock() will lock access to onEvent()