add Handler to every Logger - java

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.

Related

Hide the exception stacktrace from logs depending on log level

Let's say we have 2 projects: project A and B
Project A
This project defines some common exceptions. Doesn't have the slf4j dependency.
An example of Exception
public abstract class SomeException extends RuntimeException {
public SomeException (String msg) {
super(msg);
}
// just added, being able to hide the stacktrace, but it contains an additional param
public SomeException (String msg, boolean suppressStacktrace) {
super(msg, null, suppressStacktrace, !suppressStacktrace);
}
}
Project B
This is the main project. It includes the Project A dependency, and it calls the defined
SomeException in a lot of places. This project includes the slf4j dependency, and contains the slf4j config where we specify the logging level. I want to display the stacktrace just if the log level is DEBUG.
The challenge
I have a lot of usages of throw new SomeException(String msg), and I would like to hide the stacktrace in the Project A, based on the log level of the parent project Project B without changing the exception signature, as I will have to change it in 100 places. I am not able to have 2 super calls in a if/else statement. So the final change would look like:
public SomeException (String msg) {
// how to get the surpressStackTrace param from the parent project config?
this(msg, surpressStackTrace);
}
If this approach is not right, what other possibilities I have to hide the stacktrace without changing the called exception signature?
Note: these exceptions are non blocking. Even if the exception occurs, the flow is not interrupted. Can I use #ControllerAdvice so that after the exception is handled, the flow is continued?
I also tried this:
#ControllerAdvice
public class ControllerAdvisor {
private static final Logger logger = LogManager.getLogger(ControllerAdvisor.class);
#ExceptionHandler(SomeException .class)
public void handleSomeException(SomeException ex) {
logger.log(Level.INFO, ex.getMessage());
if(Level.DEBUG.equals(logger.getLevel())){
logger.log(Level.DEBUG, ex.getStackTrace());
}
}
}
However, this terminates the execution of the further logic. Is it possible to continue the flow after the exception is handled?
Not sure if I fully understand your question...
But here is a possible solution:
Ignores SomeException (you can because it is a RuntimeException)
Keeps the normal execution process.
Logs when the exception was thrown, with stack trace when log-level is DEBUG and without when the log-level is not DEBUG
This could be done with an Aspect
Make sure you have spring-aop on your classpath; for spring-boot, you can include spring-boot-starter-aop as dependency.
Enable AOP by adding #EnableAspectJAutoProxy to one of your configuration classes.
Write a #Around-advise to intercept all the methods where the exception can occur. Do a try-catch, but proceed with the normal execution of the code when a SomeException is thrown.
Log the message with or without the stack trace based on the log level.
This could look something like this:
package com.example.demo.aop;
import com.example.demo.exception.SomeException;
import org.aspectj.lang.*;
import org.aspectj.lang.annotation.*;
import org.slf4j.*;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class DemoAspect {
private static final Logger logger = LoggerFactory.getLogger(DemoAspect.class);
// all executed public method-calls in the package com.example.demo
#Pointcut(value = "execution(public * com.example.demo..*(..))")
private void pointcut(){
}
// intercept the defined pointcut
#Around("pointcut()")
public Object ignoreSomeException(ProceedingJoinPoint pjp) throws Throwable {
Object result = null;
try {
result = pjp.proceed(); // <-- try normal execution
} catch (SomeException e) {
log(e);
result = pjp.proceed(); // <-- continue normal execution
} finally {
return result;
}
}
// Log the exception based on the loglevel
private void log(Throwable e) {
if(logger.isDebugEnabled()) {
logger.error(e.getMessage(), e); // <-- log with stacktrace
} else {
logger.error(e.getMessage()); // <-- only log the message
}
}
}

How does java.util.logging.Handler lifecycle works?

I'm trying to implement a custom handler that logs parsed LogRecord objects into a file (basically what FileHandler or StreamHandler does). My currently implementation is shown below:
public final class ErrorHandler extends Handler {
private static final String OUTPUT_FILE = ".output";
private final Formatter formatter = new CustomFormatter();
private BufferedWriter writter;
#Override
public void publish(LogRecord record) {
if (record.getLevel() == SEVERE || record.getLevel() == WARNING) {
writeToOutput(record);
}
}
void writeToOutput(LogRecord log) {
try {
if (writter == null) {
writter = new BufferedWriter(new FileWriter(OUTPUT_FILE, true));
}
writter.write(formatter.format(log));
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void flush() {
}
#Override
public void close() {
try {
writter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
P.S.: I known that we can achieve the same as the code above just by setting filter and formatter on a FileHandler or StreamHandler however I'll need the hookpoints later in the future.
My problem is, if I leave flush() with no implementation, although output file gets created, no log is written there. If I call writter.flush() inside flush(), the log is duplicated. Any though why this might be happening?
Ok, after two days fighting agains that I came to realize that the process was running on a daemon, therefore, handler's close() was only called when daemon was killed. I believe that this was leading to multiples calls to flush() almost at the same time. Running the process with no daemon solved the issue.
My problem is, if I leave flush() with no implementation, although output file gets created, no log is written there.
This is because the bytes are cached in the BufferedWriter. Flush sends those bytes to the wrapped FileWriter. If you collect enough bytes it will flush to the target file but you risk losing that information of you have some sort of process crash or disk issue.
If I call writter.flush() inside flush(), the log is duplicated. Any though why this might be happening?
Perhaps you have added two instances of this handler to the logger and both are appending to the same file. Logger.addHandler works like a List and not like a Set. Add code to Print the logger tree which will tell you how many handler instances are installed.
I'm sure I have no process crash nor disk issue, and I believe that close calls flush. Yet, I don't see why nothing is being logged - and it happens only file is not created yet.
Close is only implicitly called when the Java virtual machine shuts down and the handler is visible from the LogManager. If the shutdown is not clean as described in the documentation then the contents of the buffered writer is not flushed.

How to initialize a handler in Android Studio?

I've been trying to do timed tasks with a handler in Android Studio, but when I try to initialize it, this happens:
private Handler handler = new Handler() {
#Override
public void publish(LogRecord record) {
}
#Override
public void flush() {
}
#Override
public void close() throws SecurityException {
}
};
Whenever I look at online examples where people use Handlers to execute code at intervals, their declarations look as such:
private Handler handler = new Handler();
How do I avoid the big jumble of methods within the Handler?
Looks like you're trying to use java.util.logging.Handler instead of android.os.Handler. Changing which one you import at the top of the file should fix your problem.
The auto-complete should show you which packages you will be importing from, so watch out for that in the future.

Global Exception Handling in an Eclipse RCP app

I want to override the global Exception Handling in my RCP app. Whenever an uncaught Exception happens I want to log it (using java logging) and then exit the app. I have already overwritten the eventLoopException(Throwable exception) method in the ApplicationWorkbenchAdvisor class. But this catches only the event loop exceptions. As of now I have also overwritten the postStartup() method like this:
public void postStartup()
{
Policy.setStatusHandler(new StatusHandler()
{
#Override
public void show(IStatus status, String title)
{
LOGGER.log(Level.SEVERE, "Uncaught Exception", status.getException());
UnexpectedErrorDialog();
PlatformUI.getWorkbench().close();
}
});
}
It logs the exception in my log file and exits the app. But it's obviously not right and the exception is shown twice in the console, cause all I do is intercepting the showing of the exception in a gui dialog to the user. So how can I properly overwrite/change the global exception handling, so that my code (log) is used instead of the default one?
I would suggest you to use org.eclipse.ui.statusHandlers extension point
Thanks to sambi reddy's tip i have now overwritten AbstractStatusHandler in the ApplicationWorkbenchAdvisor class
#Override
public synchronized AbstractStatusHandler getWorkbenchErrorHandler() {
if (myStatusHandler == null) {
myStatusHandler = new MyStatusHandler();
}
return myStatusHandler;
}
MyStatusHandler extends AbstractStatusHandler and i have overwritten the handle method like this:
#Override
public void handle(StatusAdapter statusAdapter, int style)
{
if(statusAdapter.getStatus().matches(IStatus.ERROR) && ((style != StatusManager.NONE)))
{
LOGGER.log(Level.SEVERE, "Uncaught Exception", statusAdapter.getStatus().getException());
UnexpectedErrorDialog();
PlatformUI.getWorkbench().close();
}
}
seems to work right, only downside is that i still get 2 console outputs.

Java: How to wait for fileChanged to execute?

I'd like to have my thread (the main/EDT) wait until changes to a file occur and then wait. DefaultFileMonitor extends Runnable and hence runs in a thread of its own. Here is a SSCE:
import java.io.File;
import org.apache.commons.vfs.*;
import org.apache.commons.vfs.impl.DefaultFileMonitor;
public class FileChangeListener implements FileListener {
DefaultFileMonitor fm;
public final static File logFile = new File("t.txt");
public void startListening() throws FileSystemException {
final FileSystemManager fsManager = VFS.getManager();
final FileObject listendir = fsManager.toFileObject(logFile);
fm = new DefaultFileMonitor(this);
fm.addFile(listendir);
fm.start();
}
#Override
public void fileCreated(FileChangeEvent fce) throws Exception {
fileChanged(fce);
}
#Override
public void fileDeleted(FileChangeEvent fce) throws Exception {
//hmm..why deleted?
}
#Override
public void fileChanged(FileChangeEvent fce) throws Exception {
System.out.println("fileChanged executed");
}
}
The main:
import java.io.PrintWriter;
public class App {
public static void main(String[] args) {
FileChangeListener fcl = new FileChangeListener();
try {
fcl.startListening();
final PrintWriter printWriter = new PrintWriter(FileChangeListener.logFile);
printWriter.println("Hello Threads!");
printWriter.close();
//EXECUTE THE FOLLOWING ONLY AFTER fileChanged
System.out.println("Mission complete.");
} catch (Exception ex) {
}
}
}
Append the following to App.main(..) after printWriter.close():
synchronized (fcl) {
fcl.wait();
}
//EXECUTE THE FOLLOWING ONLY AFTER fileChanged
System.out.println("Mission complete.");
and append the following to FileChangeListener.fileChanged(..) after System.out.println("fileChanged executed"):
synchronized (this) {
this.notifyAll();
}
You could communicate between teh two using "Conditions" : http://download.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Condition.html
Basically, create a new "shared" Condition (say fileChanged). Now, whenever the file changes (in fileChanged() trigger this condition (fileChanged.signal()). In your main code, wait for this condition to occur (fileChanged.await()).
Hope you get the idea.
For making the condition accessible to multiple code unit, here is what I can think (decreasing order of preference) :
Assuming you are going to need as many conditions as many files you listen to, create a factory method getCondition(String file path/name/attribute) which will return the Condition object based on the file (its path or name or other attributes). Use this factory method to get the condition in all cases. The factory should internally create new Condition() instances for each new file to be listened to AND must throw away older instances as the processing of the files is complete (so probably you should add a destroy/deleteCondition(String file) method as well.)
Store the condition as a public field in the listener class (kind of hack if you have the listener instance available).
Store the condition as a public static field in the listener class (kind of hack if you have only one listener instance throughout).
Why? FileChangeListener is a callback: it is executed when the event occurs. In this specific case you've just closed the file so you alreayd know that the mission is complete on that file, so just proceed to the next step. I don't see why you need a FileChangeListener at all here.

Categories