How to append an exception's StackTrace into a file in java? - java

In java, when we catch a exception, we usually can use printStackTrace() method to print the error information, and we can also use printStackTrace(PrintStream out) to direct those information to a file.
But how can I append those information into a existing file, just like using out.append()?

You must open file in append mode:
try {
//...
} catch (Exception e) {
try(Writer w = new FileWriter("file.log", true)) {
e.printStackTrace(new PrintWriter(new BufferedWriter(w)));
}
}
If you are not using Java 7, you must remember about closing or at least flushing the Writer. Or you can have a global Writer, but then you must synchronize it between threads.
What about simply using some existing Java library like logback, log4j or even java.util.logging? Simply say:
} catch (Exception e) {
log.error("Opps!", e);
}
...and the framework will log the exception wherever you want, with lots of additional data like thread name, timestamp, additional message, etc. Logback can also show you from which library given stack frame comes from and even print the stack trace starting from root cause (most nested exception).

We can redirect the error stream to a file
System.setErr(new PrintStream(new FileOutputStream("errors.txt", true), true));
then Throwable.printStackTrace() will print to errors.txt.
"true" in FileOutputStream constructor means "append"; "true" in PrintStream condtructor means "autoflush". We do not need to worry about never closing FileOutputStream, OS will close it when the app ends.

Related

Cannot access file because being used

I'm writing a logger to my java program (in csv format).
The logger works fine, and I had one problem.
It sounds pretty logical that the program will crash when i tried to write to the file and at the same time open the file.
When i do that, I got that exception: "The process cannot access the file because it is being used by another process".
My question is if there is anyway to continue writing even if someone open the file?
Thanks.
UPDATE:
I think i solved the problem.
Every time after i write to the file (With bufferedWriter and FileWriter), I call to a close() function that closing the bufferedWriter and FileWriter.
I changed the close() function:
1. Added fileChannel and FileLock.
2. Igonore the line bw.close();
Its ok not to close the bufferWriter (bw)?, Can there be any problems later on?
private void close() throws IOException {
RandomAccessFile rf;
rf = new RandomAccessFile(file, "rw");
fileChannel = rf.getChannel();
lock = fileChannel.lock();
try {
if (bw != null) {
// bw.close(); The line i ignored.
bw = null;
}
if (fw != null) {
fw.close();
fw = null;
}
} catch (IOException ex) {
ex.printStackTrace();
}
lock.release();
}
UPDATE 2:
Now i found that if i change the function to that (close changed to flush), Its working:
private void close() {
try {
if (bw != null) {
bw.flush();
bw = null;
}
if (fw != null) {
fw.flush();
fw = null;
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
What is the best option ?
Reverse the problem: try to open while continuing writing:
if you want fixed datas, you can copy the file (by shell), and then read it;
if you want even future written datas, you must keep the same output: try to redirect the normal output, to something you can store and read.
Perhaps some library exists. It seems like tee and tpipe.
see for example:
Could I duplicate or intercept an output stream in Java?
for redirecting log4j to what you want, see this for example:
How do I redirect log4j output to my HttpServletResponse output stream?
Is there is anyway to continue writing even if something else has opened the file?
Not in Java.
To write a file, you must first open it. If you cannot open it because the OS won't permit it ... because something else has opened it ... then you cannot get to the point where you can write it.
In this scenario, you should consider opening a different log file.
Note that this scenario happens in Windows because Java is following normal Window practice and opening the file with an exclusive (mandatory) lock by default. Short of changing Java ... and every other Windows application that opens files like this ... you are stuck.
UPDATE
It turns out that there may be a way.
Read this Q&A: https://stackoverflow.com/a/22648514/139985
Use FileChannel.open as described, but use flags that allow you to write without forbidding other writers. For example
FileChannel.open(path, WRITE)
or
FileChannel.open(path, WRITE, APPEND)
The trick is that you don't want any of the NOSHARE_* options.
CAVEAT: I haven't tried this.
As #guillaume said, you can use a library like log4j.
But If you want to implements your solution in Java, you can use the observer pattern and write your logs async.

How do I write exceptions to a text file? (JAVA)

I am supposed to Use Scanner to read int values from a file “input.txt”, Read until end of file is reached, Write all program output to a file “output.txt”, Write all exceptions to a file “error.txt”, and Output each number read.
I understand exceptions for the most part, but I do not understand how to write all exceptions to a text file in Java...
Like I wrote in my comment, write a try ... catch block where you write the exception to File error.txt using Printwriter.
For instance:
PrintWriter writer = new PrintWriter("error.txt");
try {
//Code to try goes here
} catch (Exception e) {
//You've got an exception. Now print it to error.txt
writer.write(e.toString());
}
You could test with something simple like:
PrintWriter writer = new PrintWriter("error.txt");
int[] i = new int[1];
try {
i[10] = 2;
} catch (Exception e) {
writer.write(e.toString());
}
This results in error.txt being populated with:
java.lang.ArrayIndexOutOfBoundsException: 10
You can print the entire Stack Trace by doing
e.printStackTrace(writer);
Very important that you don't forget to close the PrintWriter or your file won't get printed to. Once you are done writing to the file, close with:
writer.close();
use log4j for logging purpose,
Also , configure it at Error level for printing error into different
file.
from your java code, write below line will print whole stack-track to
the log file.
commons-logging, it's use likewise:
log.error("Message", exception);
All exceptions have a method: getMessage() that will return the exception as a string. In addition, toString(), which all Java classes should have, gives a little bit more information, since it also calls getMessage().

PrintWriter--No Output?

EDIT: Fixed! Was overwriting file instead of appending to the file.
I seem to have a bit of a problem trying to use a PrintWriter to add a "header" to a log file before use, so that my program knows that it is not just a random text file. The field output_file refers to an argument taken by the logger initialization function to set the log file, whereas log_file is a static file field for the (global and static) logging class. After opening the log file to check for errors, there is no header. Using logging functions, also carried out by a PrintWriter, gives the correct output. Am I doing something wrong? (I know I am reinventing the wheel as a logging API already exists for Java, but I am working on a learning exercise.)
//After testing to make sure the file specified does not exist
log_file=output_file;
output_file.createNewFile();
PrintWriter pw=new PrintWriter(log_file);
pw.println("**[PROGRAM_NAME Log]**");
pw.flush();
pw.close();
isInitialized=true;
EDIT: The file is definitely writable, or the logger itself wouldn't work. Also, for those who were wondering, the code to log something is the following (Same general method as writing the header AKA new PrintWriter(log_file);)
PrintWriter pw = new PrintWriter(log_file);
pw.print("[INFO:] " + sdf.format(new Date())); //The variable sdf is a date formatter for timestamps.
pw.println(" " + message);
pw.close();
The documentation for PrintWriter says:
file - The file to use as the destination of this writer. If the file
exists then it will be truncated to zero size; otherwise, a new file
will be created. The output will be written to the file and is
buffered.
So every time you use new Printwriter(log_file), you are actually truncating the file, erasing everything in it, and starting fresh.
This means, of course, that as soon as you start logging, the header data gets erased.
If you want to append to the file, rather than erase it every time, you need to open a FileOutputStream with the append option, and open the PrintWriter on top of it.
try (
FileOutputStream os = new FileOutputStream(log_file, true);
PrintWriter pw = new PrintWriter(os);
) {
pw.println("Your log line here");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
(Note the use of try-with-resources to auto-close the file when work with it is done. Of course, you can just keep it opened until you finish logging. This is merely an example.).

Rolling file implementation

I am always curious how a rolling file is implemented in logs.
How would one even start creating a file writing class in any language in order to ensure that the file size is not exceeded.
The only possible solution I can think of is this:
write method:
size = file size + size of string to write
if(size > limit)
close the file writer
open file reader
read the file
close file reader
open file writer (clears the whole file)
remove the size from the beginning to accommodate for new string to write
write the new truncated string
write the string we received
This seems like a terrible implementation, but I can not think up of anything better.
Specifically I would love to see a solution in java.
EDIT: By remove size from the beginning is, let's say I have 20 byte string (which is the limit), I want to write another 3 byte string, therefore I remove 3 bytes from the beginning, and am left with end 17 bytes, and by appending the new string I have 20 bytes.
Because your question made me look into it, here's an example from the logback logging framework. The RollingfileAppender#rollover() method looks like this:
public void rollover() {
synchronized (lock) {
// Note: This method needs to be synchronized because it needs exclusive
// access while it closes and then re-opens the target file.
//
// make sure to close the hereto active log file! Renaming under windows
// does not work for open files
this.closeOutputStream();
try {
rollingPolicy.rollover(); // this actually does the renaming of files
} catch (RolloverFailure rf) {
addWarn("RolloverFailure occurred. Deferring roll-over.");
// we failed to roll-over, let us not truncate and risk data loss
this.append = true;
}
try {
// update the currentlyActiveFile
currentlyActiveFile = new File(rollingPolicy.getActiveFileName());
// This will also close the file. This is OK since multiple
// close operations are safe.
// COMMENT MINE this also sets the new OutputStream for the new file
this.openFile(rollingPolicy.getActiveFileName());
} catch (IOException e) {
addError("setFile(" + fileName + ", false) call failed.", e);
}
}
}
As you can see, the logic is pretty similar to what you posted. They close the current OutputStream, perform the rollover, then open a new one (openFile()). Obviously, this is all done in a synchronized block since many threads are using the logger, but only one rollover should occur at a time.
A RollingPolicy is a policy on how to perform a rollover and a TriggeringPolicy is when to perform a rollover. With logback, you usually base these policies on file size or time.

FileOutputStream does not create file

I actually checked other posts that could be related to this and I couldn't find any answer to my question. So, had to create this newly:
The file does not get created in the given location with this code:
File as = new File ("C:\\Documents and Settings\\<user>\\Desktop\\demo1\\One.xls");
if (!as.exists()) {
as.createNewFile();
}
FileOutputStream fod = new FileOutputStream(as);
BufferedOutputStream dob = new BufferedOutputStream(fod);
byte[] asd = {65, 22, 123};
byte a1 = 87;
dob.write(asd);
dob.write(a1);
dob.flush();
if (dob!=null){
dob.close();
}
if(fod!=null){
fod.close();
The code runs fine and I don't get any FileNotFoundException!!
Is there anything that I'm missing out here?
You can rewrite your code like this:
BufferedOutputStream dob = null;
try {
File file = new File("C:\\Documents and Settings\\<user>\\Desktop\\demo1\\One.xls");
System.out.println("file created:" + file.exists());
FileOutputStream fod = new FileOutputStream(file);
System.out.println("file created:" + file.exists());
BufferedOutputStream dob = new BufferedOutputStream(fod);
byte[] asd = {65, 22, 123};
byte a1 = 87;
dob.write(asd);
dob.write(a1);
//dob.flush();
}
catch (Exception ex) {
ex.printStackTrace();
}
finally {
if (dob != null) {
dob.close();
}
}
In this case it is only necessary to call the topmost stream handler close() method - the BufferedOutputStream's one:
Closes this output stream and releases any system resources associated with the stream.
The close method of FilterOutputStream calls its flush method, and then calls the close method of its underlying output stream.
so, the dob.flush() in try block is commented out because the dob.close() line in the finally block flushes the stream. Also, it releases the system resources (e.g. "closes the file") as stated in the apidoc quote above. Using the finally block is a good practice:
The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs. But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.
The FileOutputStream constructor creates an empty file on the disk:
Creates a file output stream to write to the file represented by the specified File object. A new FileDescriptor object is created to represent this file connection.
First, if there is a security manager, its checkWrite method is called with the path represented by the file argument as its argument.
If the file exists but is a directory rather than a regular file, does not exist but cannot be created, or cannot be opened for any other reason then a FileNotFoundException is thrown.
Where a FileDescriptor is:
Instances of the file descriptor class serve as an opaque handle to the underlying machine-specific structure representing an open file, an open socket, or another source or sink of bytes. The main practical use for a file descriptor is to create a FileInputStream or FileOutputStream to contain it.
Applications should not create their own file descriptors.
This code should either produce a file or throw an exception. You have even confirmed that no conditions for throwing exception are met, e.g. you are replacing the string and the demo1 directory exists. Please, rewrite this to a new empty file and run.
If it still behaving the same, unless I have missed something this might be a bug. In that case, add this line to the code and post output:
System.out.println(System.getProperty("java.vendor")+" "+System.getProperty("java.version"));
Judging from the path, I'd say you are using Win 7, am I right? What version?
Then it means there is a file already in your directory

Categories