Spent some more time on the C# logging class I’ve been working on. Per Joe’s suggestions, I modified the StreamWriter so that it is now a class variable and as such, it need not be initialized every time the class is used. Instead, you can use a static constructor (the idea exists in both C# and Java, although they go by different names). In C#, you simply append the keyword ‘static’ to the class name:
public class Logger {
static Logger() {
// insert static resource initialization code here
}
}
In Java it’s called a static initialization block (more here) and it looks like this:
public class Logger {
static {
// insert static resource initialization code here
}
}
If you’d like a real life example of a Java static initialization block, check out the source for the LogManager class in the log4j package.
Anyway, now the Logger class declares a StreamWriter:
private static StreamWriter sw;
and then uses the static constructor to initialize it for writing to the text file:
static Logger() {
// load the logging path
// ...
// if the file doesn't exist, create it
// ...
// open up the streamwriter for writing..
sw = File.AppendText(logDirectory);
}
Then use the lock keyword when writing to the resource to make sure that multiple threads can access the resource:
...
lock(sw) {
sw.Write("\r\nLog Entry : ");
...
sw.Flush();
}
Now all objects that call the various static methods will be using the same private streamwriter. But you’re left with one more problem. The streamwriter is never explicitly closed. If the StreamWriter was an instance variable, then we could solve this by implementing a destructor. The destructor would take this form:
~ Logger() {
try {
sw.Close();
} catch {
// do nothing, exit..
}
}
However, in this case the StreamWriter is a static/class variable, no ‘instance’ of Logger ever exists in the system, the ~Logger destructor will never get called. Instead, when the StreamWriter is eligible for destruction the garbage collector runs the StreamWriter’s Finalize method (which itself will then presumably call the Close() method of the StreamWriter instance), which will then automatically free up the resources used by the StreamWriter.
I updated the Logger class and it’s personal testing assistant TestLogger (which has also been updated to use 3 threads). You can download them here:
Sorry to keep picking on you, let me know if you tire of the abuse… 🙂
Everything sounded great until you got to destructors… destructors act on instances (i.e. objects), not on classes. So they’re the opposite of a regular constructor, not a static constructor.
In this case, the destructor never gets called because an instance of Logger never gets created–everything is static. But if you could also new up instances of Logger, you’d find that you’d suddenly start getting errors when trying to write to sw using the static methods.
In this case, I don’t think you have any choice but to leave the StreamWriter hanging out forever. It’s not a problem though; when the CLR shuts down it will get cleaned up (not sure if the cleanup happens in happy managed land, or if the OS just releases the resources used by the whole process).
Also, in general you want to be a little bit wary about doing things like releasing streams in the destructor. The reason being, you never actually know when the destructor will get called–it could be immediately after the object becomes eligible for garbage collection, it could be an hour, it could be never. The CLR doesn’t make any promises, in general. So instead you usually want your class to implement IDisposable, and provide a Dispose() method that does the cleanup and closedown stuff. That way, as soon as you’re done using something you can be sure it gets cleaned up.
For example, imagine Logger was instance-based instead of class-based, you could do this:
using (Logger logger = new Logger(“test.log”)) {
logger.Debug(“foo.”);
} // logger.Dispose() implicitly gets called here
The MSDN page you linked to has some links at the bottom about using, dispose, etc. In particular check out “Implementing a Dispose Method”.
actually, you could use a destructor with some slight of hand…
in the class level declaration section, declare a static field of the class itself:
static private Logger _singleton;
in your static constructor, instantiate it:
_singleton = new Logger();
go ahead and create a destructor; when the singleton is cleaned up its destructor will be called.
Found your site again. Maybe the older articles should link to the newer ones, so we know we’ll all reading the latest stuff? nice work
The link to http://cephas.net/blog/2003/10/26/logging_in_c_enumerations_threadsafe_streamwriter.html doesn’t work.
I think there is no way to close the streamwriter. It’d be nice to be able to write some final lines on close, but you can’t. I’m in the middle of making my own logger, and I have the same problem.
The hacker static destructor doesn’t work, either, the streamswriter is already finalized (the file is already closed) by the time that code is run (which I cannot believe, since what right does C# have to finalize my objects when the class they belong in hasn’t even run its destructor, yet???)
Leaving streamwriter to clean up itself is just ugly. Yes, you can use the Dispose and using-statement, if the class is instantiable, but it doesn’t need to be. If you make it instatntiable, then everything that accesses the logger now must access this single instance of it, and without globals, where do you put it? Even with globals, it’s an ugly solution.
Hey Sean your singleton idea is brilliant. Works in other situations as well.
This line in your Logger.cs seems to have the variables backward:
if (logLevel >= (int)level) {
It should be
if ((int)level >= logLevel) {
I take that back. The problem is the value of OFF