
I see the central concepts as:
Entry (or Message): the set of information that makes up a single message to log. Might include a "level" (e.g. debug, info, warning, error), a "category" (e.g. arguments, program_flow, etc), a "keyword" (e.g. "Network", "UI", "Disk Cache", etc) and of course a text string.
Could be more or it could be less in specific situations. But other than that I agree with you.
Filter: determines whether or not an Entry should be logged. Probably implemented as a relatively simple functor. Sink: ultimate destination for Entries. Implementations for wirting to files, system log facilities, etc can be provided. Log: provides logging methods. Implements the Filter concept and contains zero or more Sinks which are the targets of Entries for which the Filter returns true.
I aggree with you on a conceptual level. However, the Filter concept shouldn't be part of, IMO, the logger. Or if it is, at least, there should be an alternative interface in the logger, or possibly, the filtering concept should be very simple if I don't want entry keywords and such. Fundementally, I think that writing log statements and filtering log statements are two different and orthogonal concepts. Filtering is an efficiency decision at the conceptual level. With that I mean if we had infinite computing power (which we don't) filtering wouldn't be necessary at all when sending log entries to sinks. Filtering is however something the target audience does when looking for, or at, log statements. That is, with infinite computing power, the filtering mechanism could be done at the time when administrators/programmers are looking/searching logs.
But since we don't have infinite computing power, log entries need to be filter before sending them to sinks (I suppose one could argue that there are other reasons for filtering before sending entries to sinks; but I don't see a difference). So the interface for enabling/disabling log entries should be a fundemental concept to a logging library and obviously as well designed as the actual log entry interface.
Especially, wether to send a log entry to a sink should incur minimal overhead, or else risk efficiency even if the log statement are not sent to the sinks. This is especially true if programers use many log statements.
IMO at the most fundemental layer, a logger is either enabled or disabled. If it is enabled, log entries are sent to its attached sinks. Filtering should be implemented on top of loggers. The filtering concept determines wether to send the log entry to the logger or not.
Filterring is indeed is in theory is orthogonal to formatting. With two simple but very important exceptions: 1. The data that used for filterring is as frequently needed for formatting and we do not want to collect data twice. I think that Filter concept should be extended (or another concept needs to be introduced: Non-Filterring Entry component). 2. Log need to know entry data components by name to be able to configure formatting properly While designing one needs to keep in mind following points: 1. There are entry components that are used for filterring. These should be collected before the filterring stage begins. 2. There are entry components that are not used for filterring. These should be collected before the formatting stage begins (pre/post filterring is not mandated). Any of the components we talked previosly - level, file, line keyword, thread id - could, depending on users, belong to either group. 3. We do not want to collect data for filterring and formatting twice. 4. Log should be able to refer to the entry components by name for configuration purposes. I want to be able to configure log/sink with config string: "prefix=thread_id time file(line):" 5. Sone entry components need external source for initialization (file, line, level etc), some doesn't (thread id, time). Gennadiy