Using ConsoleHandler with own PrintStream suppresses System.err - java

I want the Java ConsoleHandler to use System.out instead of err, so I implemented my own handler that calls the protected void setOutputStream(OutputStream) of the parent StreamHandler class:
public class ConsoleHandler extends java.util.logging.ConsoleHandler {
public ConsoleHandler() {
setOutputStream(System.out); // or System.err
setLevel(Level.ALL);
}
}
I remove the default console logger from and add my own to the root logger:
Logger l = Logger.getLogger("");
for (Handler h : l.getHandlers())
l.removeHandler(h);
l.addHandler(new ConsoleHandler());
System.out.println("OUT");
System.err.println("ERR");
Problem: "OUT" is always printed, but "ERR" never, independent of the output stream I set in my ConsoleHandler constructor.

The stacktrace (printed to System.err) is not printed any more, without my changes it is printed as usual
This is because setOutputStream closes the previously assigned System.err stream. This is a known issue filed under JDK-4827381: Invoking ConsoleHandler.setOutputStream(...) closes System.err. What should have happened with that bug report is that the StreamHandler.setOutputStream should call Handler.close instead of flushAndClose().
You need to wrap the existing System.err stream with a proxy that doesn't allow the stream to be closed. Or just extend StreamHandler and use the constructor that takes an OutputStream.
public class OutConsoleHandler extends StreamHandler {
public OutConsoleHandler() {
super(System.out, new SimpleFormatter());
//TODO: Read level,filter,encoding from LogManager.
}
#Override
public void publish(LogRecord record) {
super.publish(record);
super.flush();
}
#Override
public void close() {
super.flush();
}
}

Related

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.

flush System.out with own logger

I created my own little logger to use instead of System.out.println
LogManager.getLogManager().reset();
logger = Logger.getLogger(this.toString());
logger.setLevel(loglevel);
Formatter formatter = new Formatter() {
public String format(LogRecord record) {
return new Date().toString().substring(11, 20) + record.getLevel()+" " + formatMessage(record) + System.getProperty("line.separator");
}
};
logger.addHandler(new StreamHandler(System.out, formatter));
LogManager.getLogManager().addLogger(logger);
At the moment, the messages don't get flushed, so they only appear once the application gets terminated. Is there a way to flush a message after printing it out without creating a new class or adding many lines of code? I want to keep this code as short as possible...
The problem is that the StreamHandler.setOutputStream wraps the given stream in a OutputStreamWriter which according to the javadocs:
The resulting bytes are accumulated in a buffer before being written to the underlying output stream. The size of this buffer may be specified, but by default it is large enough for most purposes.
So there is no way around calling StreamHandler.flush to force those bytes to the console.
Since you don't want to create a new class you can use the ConsoleHandler which will flush to console but by default will write to error stream. You can get around that if you haven't started any other threads by doing the following:
//Global resource so ensure single thread.
final PrintStream err = System.err;
System.setErr(System.out);
try {
ConsoleHandler ch = new ConsoleHandler();
ch.setFormatter(formatter);
logger.addHandler(ch);
} finally {
System.setErr(err);
}
A subclass really is your best bet because you can end up accidentally closing the System.out by calling Handler.close() through LogManager.getLogManager().reset() or by calling StreamHandler.setOutputStream which means you won't see any output at all.
public class OutConsoleHandler extends StreamHandler {
public OutConsoleHandler(OutputStream out, Formatter f) {
super(out, f);
}
#Override
public synchronized void publish(LogRecord record) {
super.publish(record);
flush();
}
#Override
public void close() throws SecurityException {
flush();
}
}

add Handler to every Logger

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.

how to temporarily make a field thread local

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.

Java -How to get logger to work in shutdown hook?

I have a specialized logger class which uses the java.util.logging.Logger class. I want to be able to use this logger in the shutdown hook of another class. However, it doesn't seem to log at shutdown. From what I've read, there may already be a shutdown hook activated for the logger itself which is causing the issue.
How can I get this to work? Ideally, I would like it to be seen in the log file that I did in fact execute the shutdown hook when the process terminated.
Again looking at the source, the solution appears to be to define a system property java.util.logging.manager which is a subclass of LogManager which overrides the reset(); method so the Loggers continue to work on shutdown.
import java.util.logging.LogManager;
import java.util.logging.Logger;
public class Main {
static {
// must be called before any Logger method is used.
System.setProperty("java.util.logging.manager", MyLogManager.class.getName());
}
public static class MyLogManager extends LogManager {
static MyLogManager instance;
public MyLogManager() { instance = this; }
#Override public void reset() { /* don't reset yet. */ }
private void reset0() { super.reset(); }
public static void resetFinally() { instance.reset0(); }
}
public static void main(String... args) {
Logger logger1 = Logger.getLogger("Main1");
logger1.info("Before shutdown");
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
#Override
public void run() {
try {
Logger logger2 = Logger.getLogger("Main2");
logger2.info("Shutting down 2");
} finally {
MyLogManager.resetFinally();
}
}
}));
}
}
prints
Dec 11, 2012 5:56:55 PM Main main
INFO: Before shutdown
Dec 11, 2012 5:56:55 PM Main$1 run
INFO: Shutting down 2
From this code for LogManager, you can see see there is a shutdown hook which dismantles the handlers and closes them. Logger only works in shutdown if it hasn't been used before so this code is not run.
// This private class is used as a shutdown hook.
// It does a "reset" to close all open handlers.
private class Cleaner extends Thread {
private Cleaner() {
/* Set context class loader to null in order to avoid
* keeping a strong reference to an application classloader.
*/
this.setContextClassLoader(null);
}
public void run() {
// This is to ensure the LogManager.<clinit> is completed
// before synchronized block. Otherwise deadlocks are possible.
LogManager mgr = manager;
// If the global handlers haven't been initialized yet, we
// don't want to initialize them just so we can close them!
synchronized (LogManager.this) {
// Note that death is imminent.
deathImminent = true;
initializedGlobalHandlers = true;
}
// Do a reset to close all active handlers.
reset();
}
}
/**
* Protected constructor. This is protected so that container applications
* (such as J2EE containers) can subclass the object. It is non-public as
* it is intended that there only be one LogManager object, whose value is
* retrieved by calling Logmanager.getLogManager.
*/
protected LogManager() {
// Add a shutdown hook to close the global handlers.
try {
Runtime.getRuntime().addShutdownHook(new Cleaner());
} catch (IllegalStateException e) {
// If the VM is already shutting down,
// We do not need to register shutdownHook.
}
}
From my own testing
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
#Override
public void run() {
try {
Logger logger2 = Logger.getLogger("Main2");
logger2.info("Shutting down 2");
} catch (Throwable t) {
t.printStackTrace();
}
}
}));
prints
Dec 11, 2012 5:40:15 PM Main$1 run
INFO: Shutting down 2
but if you add
Logger logger1 = Logger.getLogger("Main1");
outside this block you get nothing.
Does the shutdown hook has any advantage?
You better, add the Log output in the last line in main:
The finally block will be executed in all cases,
except when the VM crashes but then the shutdown hook will
not be executed, too.
static void main(String[] argv) {
try {
startApp();
} finally {
LOGGER.info("shudown");
}
}

Categories