In my code base is the (very simplified) following:
public static void main (String[] args) {
System.out.println("Starting application");
try {
System.out.println("About to validate");
validate(args);
catch (Exception e) {
e.printStackTrace();
}
}
public static void validate(String[] args) {
System.out.println("Your first arg is " + args[0]);
if (someProblemWith(args)) {
System.out.println("Your args are wrong. It should be: ...");
throw new BadArgsException(e);
}
}
Which works fine. Note that my example code above is contrived and simply meant to show multiple log statements prior to exception and stack trace printing. This often means that my last logging statement is lost in the middle of the stack trace output. Is there an elegant way to ask the e.printStackTrace() statement to wait until the System.out has finished its work? I'm essentially looking for the stacktrace to be the very last thing printed when an error occurs. Here's a sample output of my program above:
java.lang.Throwable
....
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
Your args are wrong. It should be: ...
at java.lang.reflect.Method.invoke(Method.java:597)
at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:56)
The reason you are seeing the stack trace being printed between the System.out.println() statements, is because System.out is buffered, while System.err (used by stack trace) is unbuffered.
If you want the text to be displayed in the exact order in which things are happening, you need to "unbuffer" the System.out. The simplest way is to also just use System.err there instead of System.out.
Otherwise, call System.out.flush() before your stack traces happen in the catch clauses.
Option 2: Use the Logger class.
Option 3: Implement your own "buffer". In other words, first write everything to your own buffer, including the stack traces (using .toString() or however you wish) and then in the catch flushing you own buffer. (This is kind of redundant since you can just flush the System.out anyway).
-==-
FROM COMMENT
Sure. The Logger class can be used to create a much more robust and detailed logging experience. This is typically what is done in applications. An instance of the Logger class is grabbed from the Logger class (it is a singleton), taking as parameter the class from which is will be used. Then you log messages to it by using the .log() method. The nice thing about the Logger class is that you can set levels on it (example DEBUG, WARN...) and you are then able to filter / display only what you want. The "log" messages are then displayed in a uniform way in the console, typically in the format of:
2010-11-23 14:45:32,032 DEBUG [MyClass] Your message
The above format is from log4j, but you can use the standard Java Logger. The output should be similar, maybe a bit less. But I'm sure it can be configured.
Call e.printStackTrace(System.out);. Or, if you need it for debugging only, you can separate the process' output and error from the command line: .... 1>output.log 2>error.log
Related
How do you structure code to allow file writes from different code parts to a single log file? I thought it would be appropriate to have a single static object as the file handle, open the file once, and perform buffered writes to the file from any code section, using this static file handle.
How should the code be scoped, and how should exception handling be applied for the file writes (my attached simplified code shows multiple errors in my improper exception handling, and the static declarations seem wrong). The compiler insists on exception handling for the declaration [how?] so that exception handling can be used whenever the file handle is used (I'm totally missing something fundamental here).
Simplistically, I could choose to have every code section that needs to write to the log, merely open the file, write, and close, but that is inefficient. All examples of file writing I've seen merely bundle all open, write, close operations in a small code snippet but my file operations are distributed in multiple methods within the class.
What is the best way to declare a file handle object, and use it to write to the file from different code sections, and properly handle io exceptions?
public class MyApp3 {
// create the file handle
static FileWriter log = new FileWriter("Logfile.txt", true); // compiler flags ERROR on this line
// "unreported exception IOException; must be caught or declared to be thrown"
static BufferedWriter bufferedLog = new BufferedWriter(log);
static void Activity1() {
bufferedLog.write("Activity1 actions written to log"); // compiler flags ERROR on this line
// "unreported exception IOException; must be caught or declared to be thrown"
}
static void Activity2() {
bufferedLog.write("Activity2 actions written to log"); // compiler flags ERROR on this line
// "unreported exception IOException; must be caught or declared to be thrown"
}
public static void main(String[] args) {
Activity1(); // performs some file writes
Activity2(); // performs some file writes
try {
bufferedLog.write("Last write to file before close.");
} catch (IOException e) {
e.printStackTrace();
} finally {
bufferedLog.close(); // compiler flags ERROR on this line
// "unreported exception IOException; must be caught or declared to be thrown"
}
}
}
Consider using the logging library - they've solved all the issues you've raised in the question.
Log4j (2), Logback are examples of libraries as such
There is even one, built-in into JDK (although nearly no one really uses it - its called JULI - search for the package java.util.logging)
So, in terms of these libraries, you'll have multiple loggers associated with a file appended that will write into this "single" file in an efficient manner. You'll be able to rotate this file when it reaches a certain size, the library will delete obsolete files, etc.
Also, you will be able to define the format of the message, log exceptions, and so on and so forth. Bottom line, this is something that I highly recommend to not "re-invent the wheel" and use one of the aforementioned production ready solutions
2 Questions:
What is the difference between e.printStackTrace(System.out) and e.printStackTrace() ?
Is it safe (from the production point of view) to replace e.printStackTrace(System.out) and e.printStackTrace() with log4j using logger.error("", e) if i want to print out the stack trace in my log file?
e.printStackTrace() is same as e.printStackTrace(System.err). So, it prints to standard error. e.printStackTrace(System.out)` prints to system out.
You should never do e.printStackTrace(), System.out.print(), System.err.print() on a production code. You should always use logger methods, and configure your logger.
Why is the output not in the correct order for the following test code:
public static void main(String[] args) {
boolean test = false;
try {
assert test: "will fail with -ea enabled in VM args" ;
} catch (AssertionError e) {
e.printStackTrace();
System.out.println("idk why you would use this but...");
}
System.out.println(test + " sneaks in between");
}
run this with "-ea" enabled in the VM arguments (run config)
randomly the output is either:
java.lang.AssertionError: will fail with -ea enabled in VM args
at Main.main(Main.java:31)
FOO
BAR
(should happen) or:
java.lang.AssertionError: ERROR
FOO
BAR
at Main.main(Main.java:31)
(should not happen) and sometimes:
java.lang.AssertionError: ERROR
FOO
at Main.main(Main.java:31)
BAR
I was messing around with "assert" when this happened. I know the code is complete nonsense but it may also happen with other setups.
Seconldy consoles are not really used too much in many programs as an official thing, mostly for debugging. But it is still weird.
Is this because the try catch is running on a different thread? or is the stuff happening so fast after eachother that one thing pushes out before the other thing?
I do notice adding a Thread.sleep(1); (which needs to be thrown or caught)
does make it always go in chronological order, so...
Why does it not print the code in chronological order?
printStackTrace() prints to the error out. How that chronologically lines up with the standard out is indeterminate. You can try calling flush() on System.out, but I can't assure you the result will ever be as you expect unless you specifically print the stack trace to standard out using one of the other available methods.
So we want to use the bog-standard keytool utility that ships with a JRE. But rather than going through the trouble of finding the correct path and executable extension, spawning a subprocess, and running the executable, we collectively had the bright idea ("remember, none of us is as dumb as all of us!") to just call KeyTool's main() directly. It's implemented in Java code and also shipped with the JRE, and contains the standard "classpath" exception to the GPL so we can link against it.
Looking at the KeyTool source, there's even some provision made for this sort of thing: there are comments like "if you're calling KeyTool.main() directly in your own Java program, then [helpful reminder]" and the top-level main() is capable of propagating exceptions to calling code instead of just dying with System.exit(). Being able to just build the same command-line argument array and run KeyTool.main(stuff) instead of having to mess with platform differences seems like a very Java-esque thing to do, right?
In practice, weird things happen and we don't know why.
We want to capture any output from running KeyTool, which starts off like this:
// jdk/src/share/classes/sun/security/tools/KeyTool.java, line 331:
public static void main(String[] args) throws Exception {
KeyTool kt = new KeyTool();
kt.run(args, System.out);
}
private void run(String[] args, PrintStream out) throws Exception {
// real code here, sends to 'out'
}
The KeyTool entry points don't allow us to pass a PrintStream, it's hardcoded to use System.out. That should be okay thanks to System.setOut. We have an OutputStream subclass which feeds to a JTextComponent, but for initial coding, redirecting to a text file is fine. So our code does
PrintStream orig = System.out;
try {
System.out.println("This is the last visible console line");
System.setOut(new PrintStream("redirect_test.txt"));
System.out.println("This is now redirected!");
KeyTool.main(keytool_argv); // "-help" and "-debug" for now
}
catch all the myriad ways things might go wrong { ... }
finally {
System.setOut(orig);
System.out.println("Back to normal console output");
}
But when we run the code, the redirect_test.txt file contains only "This is now redirected!". The output from keytool's "-help" still shows up on the console, along with the before-and-after println calls.
There are some other oddities in calling KeyTool directly, like the package and class name has changed between Java 7 and Java 8, but that's easy to deal with via reflection. (The comments in the KeyTool source in Java 8 still refer to the Java 7 name, heh.) The only thing just freaky weird is how its "System.out" is strangely not affected by the same redirection that works everywhere else. (No, there are no weird import statements bringing in a special System replacement.)
Here's an online copy of Java 7's KeyTool.java if you don't happen to have OpenJDK sitting around.
You just need to redirect both System.out and System.err, since the usage instructions get printed to the standard error stream instead of the standard output stream. Try this:
PrintStream original = System.out;
PrintStream redirected = new PrintStream("redirect_test.txt")
try {
System.out.println("This is the last visible console line");
System.setOut(redirected);
System.setErr(redirected);
System.out.println("This is now redirected!");
KeyTool.main(keytool_argv); // "-help" and "-debug" for now
}
catch all the myriad ways things might go wrong { ... }
finally {
System.setOut(original);
System.setErr(original);
System.out.println("Back to normal console output");
}
i recently started using sonar as code review tool.
When i analysed my code running sonar,
it reflected printing stack trace as violation of java coding standard.
As an alternative to stack trace, I tried:
e.getcause()
but this did not clear the exception as done by stack trace
Error handling can be tricky in any environment, java included. I haven't used sonar, but I can comment on general good practices for java error handling.
e.printStackTrace() is generally discouraged because it just prints out the stack trace to standard error. Because of this you can't really control where this output goes.
The better thing to do is to use a logging framework (logback, slf4j, java.util.logging, log4j, etc) because then you can control where the errors are logged to and what the log retention policy is.
And generally you'll want to catch the exception and if it's unexpected behavior, log it and either throw a new exception (probably specific to your application) or do whatever you have to do to continue operating gracefully.
If you're using java.util.logging, you can do something like the following:
class YourClass
{
Logger logger = Logger.getLogger(YourClass.class.getName());
...
public void someMethod() throws YourException
{
try
{
// your code here
} catch (NullPointerException e)
{
String message = "Unexpected NullPointerException in processing!";
logger.log(Level.ERROR, message, e);
throw new YourException(message, e);
}
}
}
Hope this helps!
A few thoughts:
I presume from the title you were using e.printStackTrace(). This does not "clear the exception", so I'm not sure exactly what your issue really is on that point. In java "clear the exception" doesn't make any sense at all in this context.
e.printStackTrace() is "not a good idea" because it writes to standard out. Much better to write such detail to a log file for later diagnostics, rather than put it out in front of a user (though that could depend on how the program actually runs). Your run-time environment may have something to say about use of standard output.
e.getCause() will return, if available, an "underlying exception" that may have been a "root cause" for the exception e. Must stack traces will show this after an initial stack dump denoted by "Caused by: : ..."
If you choose to try to capture and display/log an exception yourself - you might use e.printStackTrace(PrintStream s) or e.printStackTrace(PrintWriter s).
You'd be best served using a logging tool, as suggested by Matt.