Sign in to follow this  

Exceptions, Logs, and Sinks (Oh my!)

This topic is 4599 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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 [I have logs which are referenced by name and do stuff like your Log, but don't have sinks, just other log types, some of which log to logs etc.]. 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 on the system's shutdown.

Hope this helps, though I'm not too sure it's even coherent,

CJM

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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. :)

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites

This topic is 4599 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this