I've come to the conclusion after reading from many sources that using printStackTrace for error handling is bad practice. Here's one.
Now I'm struck curious: in what cases is printing the stacktrace a valid solution? For the sake of the argument, let's assume we aren't working on a system such as a microwave or a banana, but a basic out-of-the-shelf PC.
The reason I'm asking this could be seen as a question in itself, but I'll tell you about it anyhoo:
I'm developing a snake-like game that can be played with AIs, and is intended for that purpose. All such AIs should extend an abstract class called SnakeLogic. All such AIs should also reside in their standalone .jar archives in a specific folder, from where the main program can find them and list them using classloaders.
The user can then choose one of his/her AIs from a list, should all stars fall in line, and play a game with this AI.
Now, I have a method in my main program that gets the next move from the AI like so:
public void startGame(int speed) {
gameInterface.showWindow();
Runnable moveCmd = () -> {
try {
for (Player player : snakeGame.getPlayers()) {
if (player.isDead()) {
continue;
}
String move = player.getLogicHandler().getMove();
Direction direction = Direction.directionFromString(move);
snakeGame.makeMove(player, direction);
}
gameInterface.getFrame().repaint();
snakeGame.wait(speed);
if (snakeGame.gameOver()) {
stopGame();
}
} catch (Exception ex) {
ex.printStackTrace();
stopGame();
}
};
/* moveSchedule is an instance of ScheduledExecutorService */
moveSchedule.scheduleAtFixedRate(moveCmd, 1000, speed, TimeUnit.MILLISECONDS);
}
I'm not going to get too involved with the code above. I'd like to draw your attention to the try-catch statement, however. As you can see I print the stacktrace and end the game, should an exception occur somewhere during the execution of the moveCmd runnable. This is the source of my curiosity: If I don't print the stacktrace like this, or if I remove the try-catch entirely, I never get any errors in the case of a runtime exception during the execution of that block. Why? Is it because it's wrapped inside the runnable? Note also that the line snakeGame.makeMove(player, direction); doesn't call any code in the main program; snakeGame is an instance of a SnakeLogic, which resides in an external .jar.
Why don't I get any errors if I remove the try-catch? Also, in this case, is printing the stacktrace a good idea?
I understand this imposes two questions for you: the topic and the above. I want to emphasize the topic, so don't get too sidetracked with the second question; though insight is duly noted, there's nothing broken in my code.
You need to shift your thought process a bit when dealing with error and exceptions. It is always a good practice to print the error trace. Now the question is where to print. by default printStackTrace prints to your standard console. of course you can redirect that output to a log file like Tomcat does but that is a work around, if you ask me.
In production and pre-prod systems and even in distributable spftware where you distribute a desktop application to users for running on PCs you may or may not have dedicated access to console. Further more what prints on console is lost once the console is closed or app finishes. You need to persist the errors somewhere for analysis later. Normally folks design the app to zip and send error logs periodically to developers for analysis.
Now if you think about the whole scenarios the bottom line is to preserve the errors somewhere for analysis later. So usually do it in a rotating log file or in DB. Console wont suffice. Thus incidentally the catch block should have a log statement to log the exception.
The problem with Exception.printStackTrace() is that it writes to your console (most probably) which is a synchronous operation. Not to mention that writing to console is slow in most platforms. You dont want to hold off your execution thread until the full stack trace is written. So its better to hand it over to a log framework like log4j which has the ability to write the complete stack trace into to file asynchronously (other appenders are available), so that the execution thread returns immediately to the callee and yet the log contains necessary details.
So its a question of synchronous write or asynchronous write. As Nazgul pointed out, you have to log exceptions in a system for later analysis where ever applicable.
NotE: A problem with asynchronous logging is that if the process dies abruptly, like in kill -9 or system powered down, you may loose the buffered content before OS has chance to write it to disk
Related
I have try/catch blocks at lower levels to handle most errors, but I was told we need one near the top, basically as a catch all to allow the program to continue operating correctly if there is an error. I put a try/catch(Exception e)/finally around everything at the top level, but we are still getting exceptions causing crashes. I've been looking at the stack trace for any clues... It starts like this (I can post more of it if that would help):
111858 [SimpleAsyncTaskExecutor-2] DEBUG o.h.e.jdbc.spi.SqlExceptionHelper - could not execute statement [n/a]
java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("DATABASE"."TABLE"."COLUMN")
The weird thing is that the rest of the stack trace never goes back to anything that is ours. It's all oracle/apache/hibernate/springframework until it ends up with:
at java.lang.Thread.run(Thread.java:795) [na:1.7.0]
Any ideas why it's not going back to our code or how better to implement this "catch all"? The finally block is for some steps that we want to implement whether there is an error or not.
Edit: for clarity's sake and because maybe it will help, this is part of a batch that processes files every 5 minutes. The try/catch is like so:
try{
//process file
}catch(Exception e){
//log exception
}finally{
//mark file as processed so it doesn't keep trying to reprocess a broken file
}
You, or a library that you are using must be spawning off a thread somewhere and it is that thread that is throwing the exception.
Your options are
track down the creation of that thread, and handle the exception from there
track down which thread group that the thread belongs to and register an exception handler with that thread group
The best idea I can suggest for implementing this "catch all" is don't do it.
I would, instead, recommend defensive programming at the class level. Each class should handle its own possible exceptions when possible. This will save you time later when something goes wrong and you have to track down where to came from.
You need to debug and find where the thread is being created and handle exceptions there.
We are integrating with a 3rd party API and have purchased the license to use their Java library (wraps the API calls in object oriented fashion).
The only problem with this Java lib is that it doesn't throw exceptions or faults if the API calls fail. The only indication we get from the client-side is output to either STDOUT (on success) or STDERR (on fail).
Hence, running:
try {
com.the.third.party.java.lib.FizzBuzz.doSomething();
} catch(Throwable t) {
log.error(t);
}
...never catches anything. If an error occurs while doSomething() is running, we'll simply see the following String printed to STDERR:
Error: <details of the error here>
So I'm wondering if there is a way to monitor STDERR (System.err) for any output that contains the substring "Error:"? This could get somewhat complicated due to stream buffering, so I'm not sure what the correct approach should be.
First, if there's another way to detect errors that doesn't involve doing this, do that instead. Your future self will thank you.
But if you really need to listen to System.err, you might want to use System.setErr(PrintStream). Write your own PrintStream subclass that looks for "Error:" in the text being printed.
There is a lot of material out there which suggests that printing the stack trace of an exception is bad practice.
E.g. from the RegexpSingleline check in Checkstyle:
This check can be used [...] to find common bad practice such as calling ex.printStacktrace()
However, I'm struggling to find anywhere which gives a valid reason why since surely the stack trace is very useful in tracking down what caused the exception. Things that I am aware of:
A stack trace should never be visible to end users (for user experience and security purposes)
Generating a stack trace is a relatively expensive process (though unlikely to be an issue in most 'exceptional' circumstances)
Many logging frameworks will print the stack trace for you (ours does not and no, we can't change it easily)
Printing the stack trace does not constitute error handling. It should be combined with other information logging and exception handling.
What other reasons are there for avoiding printing a stack trace in your code?
Throwable.printStackTrace() writes the stack trace to System.err PrintStream. The System.err stream and the underlying standard "error" output stream of the JVM process can be redirected by
invoking System.setErr() which changes the destination pointed to by System.err.
or by redirecting the process' error output stream. The error output stream may be redirected to a file/device
whose contents may be ignored by personnel,
the file/device may not be capable of log rotation, inferring that a process restart is required to close the open file/device handle, before archiving the existing contents of the file/device.
or the file/device actually discards all data written to it, as is the case of /dev/null.
Inferring from the above, invoking Throwable.printStackTrace() constitutes valid (not good/great) exception handling behavior, only
if you do not have System.err being reassigned throughout the duration of the application's lifetime,
and if you do not require log rotation while the application is running,
and if accepted/designed logging practice of the application is to write to System.err (and the JVM's standard error output stream).
In most cases, the above conditions are not satisfied. One may not be aware of other code running in the JVM, and one cannot predict the size of the log file or the runtime duration of the process, and a well designed logging practice would revolve around writing "machine-parseable" log files (a preferable but optional feature in a logger) in a known destination, to aid in support.
Finally, one ought to remember that the output of Throwable.printStackTrace() would definitely get interleaved with other content written to System.err (and possibly even System.out if both are redirected to the same file/device). This is an annoyance (for single-threaded apps) that one must deal with, for the data around exceptions is not easily parseable in such an event. Worse, it is highly likely that a multi-threaded application will produce very confusing logs as Throwable.printStackTrace() is not thread-safe.
There is no synchronization mechanism to synchronize the writing of the stack trace to System.err when multiple threads invoke Throwable.printStackTrace() at the same time. Resolving this actually requires your code to synchronize on the monitor associated with System.err (and also System.out, if the destination file/device is the same), and that is rather heavy price to pay for log file sanity. To take an example, the ConsoleHandler and StreamHandler classes are responsible for appending log records to console, in the logging facility provided by java.util.logging; the actual operation of publishing log records is synchronized - every thread that attempts to publish a log record must also acquire the lock on the monitor associated with the StreamHandler instance. If you wish to have the same guarantee of having non-interleaved log records using System.out/System.err, you must ensure the same - the messages are published to these streams in a serializable manner.
Considering all of the above, and the very restricted scenarios in which Throwable.printStackTrace() is actually useful, it often turns out that invoking it is a bad practice.
Extending the argument in the one of the previous paragraphs, it is also a poor choice to use Throwable.printStackTrace in conjunction with a logger that writes to the console. This is in part, due to the reason that the logger would synchronize on a different monitor, while your application would (possibly, if you don't want interleaved log records) synchronize on a different monitor. The argument also holds good when you use two different loggers that write to the same destination, in your application.
You are touching multiple issues here:
1) A stack trace should never be visibile to end users (for user experience and security purposes)
Yes, it should be accessible to diagnose problems of end-users, but end-user should not see them for two reasons:
They are very obscure and unreadable, the application will look very user-unfriendly.
Showing a stack trace to end-user might introduce a potential security risk. Correct me if I'm wrong, PHP actually prints function parameters in stack trace - brilliant, but very dangerous - if you would you get exception while connecting to the database, what are you likely to in the stacktrace?
2) Generating a stack trace is a relatively expensive process (though unlikely to be an issue in most 'exception'al circumstances)
Generating a stack trace happens when the exception is being created/thrown (that's why throwing an exception comes with a price), printing is not that expensive. In fact you can override Throwable#fillInStackTrace() in your custom exception effectively making throwing an exception almost as cheap as a simple GOTO statement.
3) Many logging frameworks will print the stack trace for you (ours does not and no, we can't change it easily)
Very good point. The main issue here is: if the framework logs the exception for you, do nothing (but make sure it does!) If you want to log the exception yourself, use logging framework like Logback or Log4J, to not put them on the raw console because it is very hard to control it.
With logging framework you can easily redirect stack traces to file, console or even send them to a specified e-mail address. With hardcoded printStackTrace() you have to live with the sysout.
4) Printing the stack trace does not constitute error handling. It should be combined with other information logging and exception handling.
Again: log SQLException correctly (with the full stack trace, using logging framework) and show nice: "Sorry, we are currently not able to process your request" message. Do you really think the user is interested in the reasons? Have you seen StackOverflow error screen? It's very humorous, but does not reveal any details. However it ensures the user that the problem will be investigated.
But he will call you immediately and you need to be able to diagnose the problem. So you need both: proper exception logging and user-friendly messages.
To wrap things up: always log exceptions (preferably using logging framework), but do not expose them to the end-user. Think carefully and about error-messages in your GUI, show stack traces only in development mode.
First thing printStackTrace() is not expensive as you state, because the stack trace is filled when the exception is created itself.
The idea is to pass anything that goes to logs through a logger framework, so that the logging can be controlled. Hence instead of using printStackTrace, just use something like Logger.log(msg, exception);
Printing the exception's stack trace in itself doesn't constitute bad practice, but only printing the stace trace when an exception occurs is probably the issue here -- often times, just printing a stack trace is not enough.
Also, there's a tendency to suspect that proper exception handling is not being performed if all that is being performed in a catch block is a e.printStackTrace. Improper handling could mean at best an problem is being ignored, and at worst a program that continues executing in an undefined or unexpected state.
Example
Let's consider the following example:
try {
initializeState();
} catch (TheSkyIsFallingEndOfTheWorldException e) {
e.printStackTrace();
}
continueProcessingAssumingThatTheStateIsCorrect();
Here, we want to do some initialization processing before we continue on to some processing that requires that the initialization had taken place.
In the above code, the exception should have been caught and properly handled to prevent the program from proceeding to the continueProcessingAssumingThatTheStateIsCorrect method which we could assume would cause problems.
In many instances, e.printStackTrace() is an indication that some exception is being swallowed and processing is allowed to proceed as if no problem every occurred.
Why has this become a problem?
Probably one of the biggest reason that poor exception handling has become more prevalent is due to how IDEs such as Eclipse will auto-generate code that will perform a e.printStackTrace for the exception handling:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
(The above is an actual try-catch auto-generated by Eclipse to handle an InterruptedException thrown by Thread.sleep.)
For most applications, just printing the stack trace to standard error is probably not going to be sufficient. Improper exception handling could in many instances lead to an application running in a state that is unexpected and could be leading to unexpected and undefined behavior.
I think your list of reasons is a pretty comprehensive one.
One particularly bad example that I've encountered more than once goes like this:
try {
// do stuff
} catch (Exception e) {
e.printStackTrace(); // and swallow the exception
}
The problem with the above code is that the handling consists entirely of the printStackTrace call: the exception isn't really handled properly nor is it allowed to escape.
On the other hand, as a rule I always log the stack trace whenever there's an unexpected exception in my code. Over the years this policy has saved me a lot of debugging time.
Finally, on a lighter note, God's Perfect Exception.
printStackTrace() prints to a console. In production settings, nobody is ever watching at that. Suraj is correct, should pass this information to a logger.
It is not bad practice because something is 'wrong' about PrintStackTrace(), but because it's 'code smell'.
Most of the time the PrintStackTrace() call is there because somebody failed to properly handle the exception. Once you deal with the exception in a proper way you generally don't care about the StackTrace any more.
Additionally, displaying the stacktrace on stderr is generally only useful when debugging, not in production because very often stderr goes nowhere. Logging it makes more sense. But just replacing PrintStackTrace() with logging the exception still leaves you with an application which failed but keeps running like nothing happened.
In server applications the stacktrace blows up your stdout/stderr file. It may become larger and larger and is filled with useless data because usually you have no context and no timestamp and so on.
e.g. catalina.out when using tomcat as container
As some guys already mentioned here the problem is with the exception swallowing in case you just call e.printStackTrace() in the catch block. It won't stop the thread execution and will continue after the try block as in normal condition.
Instead of that you need either try to recover from the exception (in case it is recoverable), or to throw RuntimeException, or to bubble the exception to the caller in order to avoid silent crashes (for example, due to improper logger configuration).
I am running Ubuntu 10.10 using Java 6 and can not get FreeTTS to output any audio. I have tried it now on 3 different computers and even asked a buddy of mine to try it on his Ubuntu PC and he had the same problem. There is absolutly no errors that are displayed, after getting the MBROLA i no longer even get the warning about No MBROLA voices detected. blah blah blah..
Using the same computer I ran a virtual box and started Windows XP, i was actually able to get audio when running the HelloWorld.jar and TTSHelloWorld.jar however the freetts.jar is still silent when I try to input my own text.
Command I use.
java -jar lib/freetts.jar -text Hello
When I hit enter it starts up and used to give me the missing MBROLA warning message but now it just sits there until i CTRL-C to stop it.
I dont understand what I am doing wrong and why nobody else is having this problem, when I expierence it on every computer, well it works somewhat on Windows. Can anyone Help me?
Thanks,
John
I'm not sure whether you already managed to solve this one, but I ran into the same problem (Ubuntu 10.10 / JavaSE6). After some investigation of the FreeTTS source I found the culprit, a deadlock, in com.sun.speech.freetts.audio.JavaStreamingAudioPlayer. This deadlock occurs when a Line is opened and the Line is of the type org.classpath.icedtea.pulseaudio.PulseAudioSourceDataLine (which is likely to be the default in Ubuntu 10.10 w JavaSE6). Since you'd always want to open a Line to get audio out, this deadlock will always occur.
The cause of this deadlock lies in the fact that in the JavaStreamingAudioPlayer an assumption is made about Line, namely that all LineListeners will be notified of a LineEvent of type open from the same Thread as Line.open() was called, or after the Line has been opened (and the call to Line.open() can return). This is not the case for the PulseAudioSourceDataLine; it first calls all LineListeners from the PulseAudio event Thread, waits for all of them to return and then returns from the open call. With the JavaStreamingAudioPlayer forcing synchronization around the call of Line.open() and the handling of a specific LineListener which task is to see whether the Line ís actually open, the deadlock occurs.
The workaround I chose for solving this problem is to implement an AudioPlayer which doesn't has this problem. I basically copied JavaStreamingAudioPlayer and altered the synchronization blocks on line 196 and line 646 ( full source for reference : http://www.javadocexamples.com/java_source/com/sun/speech/freetts/audio/JavaStreamingAudioPlayer.java.html ).
___: // This is the actual JavaStreamAudioPlayer source, not the fix
195: ...
196: synchronized (openLock) {
197: line.open(format, AUDIO_BUFFER_SIZE); // Blocks due to line 646
198: try {
199: openLock.wait();
200: } catch (InterruptedException ie) {
201: ie.printStackTrace();
202: }
203: ...
643: ...
644: public void update(LineEvent event) {
645: if (event.getType().equals(LineEvent.Type.OPEN)) {
646: synchronized (openLock) { // Blocks due to line 196
647: openLock.notifyAll();
648: }
649: }
650: }
651: ...
I removed both synchronization blocks and instead of ensuring both parts are mutually excluded I used a Semaphore to signal that the Line is in fact open. Of course this is not really a necessity since the PulseAudioSourceDataLine already guarantees being opened upon returning, but it is more likely to play nice when testing the same code on another platform. I didn't dive into the code long enough to say what is going to happen when you open/close/open the line by multiple Threads at the same time. If you're going to do this you are probably looking at a larger rewrite of the JavaStreamingAudioPlayer ;).
Finally, after you have created your new AudioPlayer you'll have to instruct FreeTTS to use your implementation rather than the default JavaStreamingAudioPlayer. This can be done by using
System.setProperty("com.sun.speech.freetts.voice.defaultAudioPlayer", "classpath.to.your.AudioPlayer");
somewhere early in your code.
Hopefully this all works for you.
I am a student who has been trying to make FreeTTS working on its Ubuntu for one week. And finally I found the answer here : thank you so much hakvroot !
Your answer was perfect but you did not put your implementation and this took me quite one hour to understand what was going on in the JavaStreamingAudioPlayer class. To help the other people like me who are not used in "diving" in a completely unknown Java code (I am still a student), I will put here my code and hope it will help other people :) .
First, a more detailed explanation : around line 152, the JavaStreamingAudioPlayer opens a Line. However this operation can require some time so before using it, it wants to check it is opened. In the current implementation, the solution used is to create a LineListener listening to this line and then to sleep (using the wait() method of the threads).
The LineListener will "wake up" the main Thread using a notifyAll() and will do this only when it receives a LineEvent of type "OPEN" which will guarantee that the line has been opened.
However as explained by hakvroot here the problem is that the notification is never sent because of the specific behavior of the DataLine used by Ubuntu.
So I removed the synchronized, wait() and notifyAll() parts of the code but as hakvroot, then your JavaStreamingAudioPlayer might try to use your Line before it is opened : you need to wait for the confirmation with a new mechanism to stop the JavaStreamingAudioPlayer and to wake it up later, when the confirmation arrived.
So I used the Semaphore which havkroot used (see Javadoc for explanations on this lock system) initiated with 1 stack :
when the line is opened it acquires one stack (so 0 remains)
when it wants to use the line it tries to acquire another (so it is stopped)
when the listener gets the event we are looking for, it releases the semaphore
this frees the JavaStreamingAudioPlayer who can go for the next part
do not forget to release again the semaphore so it has again 1 stack for the next line to open
And here is my code :
Declare a Semaphore variable :
private Semaphore hackSemaphore;
Initiate it in the constructor :
hackSemaphore = new Semaphore(1);
Then the first part to replace (see hakvroot to see where to put it) :
line = (SourceDataLine) AudioSystem.getLine(info);
line.addLineListener(new JavaStreamLineListener());
line.open(format, AUDIO_BUFFER_SIZE);
hackSemaphore.acquire();
hackSemaphore.acquire();
opened = true;
hackSemaphore.release();
And the second part :
public void update(LineEvent event) {
if (event.getType().equals(LineEvent.Type.OPEN)) {
hackSemaphore.release();
}
}
I guess had the same issue on Ubuntu 12.04/OpenJDK-6, the execution get stuck in Voice.allocate() with no errors and no response.
I tried using the Oracle/Sun JDK-6 instead of OpenJDK, and it worked fine.
P.S. Nice guide to install SunJDK on Ubuntu and configuring as default
http://www.devsniper.com/ubuntu-12-04-install-sun-jdk-6-7/
What is the best way to have a (Java) program recognize it crashed last time it ran and show a message along the lines of "it looks like this program crashed on you last time. Report this problem here: bla#foo.com ...."
Is there a recommended way of doing this? (Bad?) ideas I had would be:
Have the program store a temporary key file at startup and then delete it when closing regularly. If the file exists at startup, show the message.
Recognize deadlocks and store an "error file" in this case. If an "error file" exists at startup, show the error message and move the file into an archive or something similar.
There are three reasons why a Java program can crash:
Unhandled RuntimeException. This is easy to solve with a try-catch in main.
Unhandled Errors. These are rare but can be caught in main also. I usually catch Throwable in main. See below for a template.
If you use threads, then look at Thread.setDefaultUncaughtExceptionHandler().
Bugs in the VM, or program killed by the user, or hardware violent shutdown. These will lead to a crash which can't be caught. Here, your best option is to create a flag file somewhere with new File(...).deleteOnExit(). Java will clean it up for you if it gets a chance.
The problem with deadlocks is how to detect that you have a deadlock. I haven't seen a consistent way to do that, yet.
import org.apache.commons.lang.exception.ExceptionUtils;
public class Demo
{
public static void main (String[] args)
{
try
{
Demo obj = new Demo ();
obj.run (args);
System.out.println ("Done.");
}
catch (Throwable t)
{
ExceptionUtils.printRootCauseStackTrace (t);
}
}
}
Crash as in an uncaught exception? The use a Thread.setDefaultUncaughtExceptionHandler, and display the message as part of the crash.
On the first idea, how do you handle multiply instances of the applications running at the same time? (Also think about multi-user environments).
Recognize deadlocks - How often are deadlocks the problem? I guess you could monitor the thread states on all the "key" threads.
You then have external forces killing the application, should they be considered a problem that you should report? After all your application was not at fault in this case.
Finally, always store an "error file" in the form of a log. Use a proper logging framework (i.e. Java Logging or Log4J). You could check the last lines of this for a signal that the application exited normally but again you will need to be careful in multi-instance environments.
A variant of the first solution you propose is common enough on Un*x for processes: store the pid file of a running process in a file at startup. When the program is launched again you can check if this file still exists (and even if the process with this pid is running).
With Java you could probably adapt this idea using Threadid defined in ThreadMXBean. But any file would do. A file that contains a key as you propose seems a good enough way. You could also put some usefull information in it like last execution time. If it's still there at startup the program didn't stopped cleanly.
It could also become something like a launch log file that trace program events including startup and clean stops, and maybe locks.
What I do is redirect System.err to a file, so that any error message (like crashes) end up in a file I can later process...
The code to do this is quite simple...
String errLog = "c:\\myLog";
try
{
java.io.PrintStream err = new java.io.PrintStream(new java.io.FileOutputStream(errLog));
System.setErr(err);
}
catch (java.io.FileNotFoundException fnfe) {}
I'm going to mimic Marcos here. Create a configuration or log file that will host the last error message and last run date of the program. Then read that file during program load.
Many of these answers are about tracking exceptions that caused your app to quit working.
Another possibility is that the application just quit (i.e. user killed it, computer shutdown, power outage, etc.). I think your temporary key idea will work for that. It's similar to how text editing programs, such as vi or Word, automatically create a special copy of the file being edited. Upon opening it checks to see if the special copy exists and asks if you want to recover the file.