Exceptions, Logs, and Sinks (Oh my!)

Started by
4 comments, last by cozman 18 years, 11 months ago
I'm having a frustrating design problem, and I was wondering if anyone had any suggestions. I currently have a Log/Sink system. For those unfamiliar with this idea I basically have two classes Log and the abstract LogSink. At the moment Log is a singleton, and it has a way for me to pass messages to it (for this example we'll just say there's a write(std::string msg) member of Log). When Log gets a message it passes it on to it's various Sinks (in reality some filtering might be done here). A Sink is an instance of a class which is derived from LogSink, which simply means it is capable of outputting the message in some way. (ConsoleSink simply writes any messages to the console via cerr) At the moment I have a piece of code that wraps all other code that catches any stray exceptions, then it logs the exception, and the program exits. The trouble is that if an error occurs before the user has set a log, the program does not crash, and the program does not log a message. An example follows:

class MyApp : public Application
{
    int main(const ArgList& args)
    { 
         ConsoleSinkPtr sink = new ConsoleSink();
         Log::getInstance().addSink(sink);

         error_filled_proc();

         return 0;
    }
};

Assuming an error occurs within error_filled_proc(), it will be logged, but if an error occurs within the Application's constructor no error will be logged. I've come up with two options: - Remove the safety-net code which is around the invocation of MyApp (bad because the whole point was to keep the user of the framework from having to worry about catching the exceptions the library may throw) - Add a default sink to the Log (log errors to console? to a text file? both? default behavior is hard to settle on, and if the user isn't seeing the errors it still looks like their opening and closing without crashing or giving an explanation) If anybody has any suggestions I'd really appreciate it.. I have a feeling I'm going to need to rework some of the design, but maybe there's an obvious solution I'm missing.
Advertisement
Instead of your constructor throwing an exception, could it set a flag (or even a string so that you'd have a detailed message) to a failed state and then in your main method you could check this flag once the log has been created?

A bit of a messy solution, as if the user calls another function between the class being constructed and the main function, what will happen? You don't really want to check in every function if the class is in a failed state as code becomes very messy.

Another solution, which I think is nicer, is force the user to set up a log and pass that into your constructor. I don't know if that's feasable or not, but would be they way I would do it. Perhaps you could still provide a default constructor that just throws an exception?

HTH
Hey,

Although I must admit that I don't entirely understand how your system works [I'm wondering in particular how error_filled_proc() knows where to throw the error] but my solution is to log them to a statically allocated global sink. Maybe even a special sink which just stores them until you add another sink...

This may be a really stupid idea under the system that you've got, but it's kind of how my logging system works . If a log name that doesn't exist is referenced in a write statement of mine, the system stores the message until the log with that name is created. It is then immediately written. Oh, I also have a global log where I state that it's an error to write to a log that doesn't exist yet, but that's not really the issue here. Also, if you do implement a system like this, then make sure that you flush any unwritten items to somewhere that they'll be really obvious &#111;n the system's shutdown.<br><br>Hope this helps, though I'm not too sure it's even coherent,<br><br>CJM
Thanks for the quick responses guys

desertcube: Unfortunately the system is designed so that the user does not instantiate the class, the first code that the user writes that is going to be run is within MyApp::main, which is exactly the problem.

CJM: I see what you're saying, it makes sense, and it's actually similar to what I'm planning.

What I've done for now (barring any other ideas) is to scratch the idea of Log being a singleton and make an instance of Log that has a ConsoleSink that serves as the catch-all.
Two suggestions:

1. Define an exception type (e.g. ApplicationException) and make this exception type part of your Application interface; document the Application constructor to state that it may throw an ApplicationException if initialisation fails. Re-throw any unlogged initialisation-time exceptions as ApplicationExceptions and leave the client to catch them. This is the "watch my back while I set up a safety net" approach.

2. Log to std::cerr by default; if the user wants a log file, he can always pipe the output... alternatively you could do #1 and let the client log the ApplicationException to std::cerr.

my 2p. :)
Quote:Original post by ajones
Two suggestions:

1. Define an exception type (e.g. ApplicationException) and make this exception type part of your Application interface; document the Application constructor to state that it may throw an ApplicationException if initialisation fails. Re-throw any unlogged initialisation-time exceptions as ApplicationExceptions and leave the client to catch them. This is the "watch my back while I set up a safety net" approach.

2. Log to std::cerr by default; if the user wants a log file, he can always pipe the output... alternatively you could do #1 and let the client log the ApplicationException to std::cerr.

my 2p. :)


Those are two really good suggestions, unfortunately #1 doesn't fit here because the system I'm using basically redefines the entrypoint of the application to be within Application, so if Application's constructor throws anything I can't rely on the user.

Good point, I was afraid of committing to just cerr, but that's what I've done. I have some more advanced Sinks that they aren't going to be able to pipe too, but I realized that well-formatted output isn't necessary if something goes wrong in init. There will be at most two entries in the log file, so the lack of color-coding really shouldn't hurt anyone [smile]

This topic is closed to new replies.

Advertisement