At the moment, I am writing a program to execute sql statements. It parses the input and executes each statement using my own database engine (fun project).
I wonder where is the best place to generate error messages.
I thought of three approaches: 1) catch all possible errors in main and print the appropriate message based on some context (lexer, parser etc); 2) pass some stream (stderr) to most calls and print the error message using the stream directly in the place where the error happens; 3) change the prototype of most functions in order to return value or error message, and print the error message in main when it is available.
Which is your preferred approach? Is there another?
I think any of these approaches are viable. Personally, I use the second one so the information I want to include in the message is readily and easily available. However, instead of passing in stderr I use std.log.err().
I also use the std.log.err and std.log.debug because it is global and available without any configuration.
To add another option, or just food for thought. If you are running a service, treat logging separately from your application. In Windows, there was the system event log. I think for Linux it’s syslogd. In the very distant past, I remember we paid for a commercial third party library called “Paul Bunyan” with zero external dependencies expressly to deal with logging.
Just a little (off topic) note: nowadays it is replaced with journald (at list in all Debian based distros, do not know about others). Everything that a service (i.e something run via systemd) outputs to stdout/stderr goes to journald. But sometime ago I reifused to use this universal logging facility, because:
journal is binary => journalctl to view logs
it may be very slow to do journalctl -u some.service | grep some-string
Instead I log into predefined files, and my logging mechanism include filtering by log-level and also log rotating.
I tested the std.log.err approach and it seems very clean! I just implemented the error messages in the lexer and parser layers, each one generating messages in it’s own scope. I still need to write code for the other components (binder, optimizer, executor) but I think I got it.
Thank you very much for your suggestions and interesting info.