Multithread enabled logging facility.
The logging module offers a very advanced facility for logging application messages to various device.
It is based on a Area/Channel architecture.
The LogArea class provides a logical subdivision of the semantic meaning of a log entry; sending a log to a certain area means that the entry has as certain meaning and function in the application architecture. For example, an application can have "security", "trace" and "status" areas; security area is meant to log informations and errors concerning security policy operations (login, logout, password change and so on). The status area may be dedicated to log internal operation status, as, for example failures in opening a database, performed queries or amount of records changed. The trace area may be specifically dedicated to debug or deep audit.
Channels are physical or logical device managers, receiving log requests and delivering them on final media, rendered as their internal rules dictate.
The user application posts a log entry to an area, and the area dispatches the entry to all the channels that are registered with. Channels can be registered with multiple areas, and they can be dynamically added to new areas or removed from areas they are currently connected to.
Each message it's associated with a "level", also called severity, which determines how important the log entry is. Channels can be configured to perform log operations only to messages having at least a minimum level, that is, severe at least as a certain level. Lower level entries are ignored by the channel.
The current logger module provides one default area, called GeneralLog, and three log channel classes:
The logger module is thought to perform the final part of the log operations in the most efficient way possible. Each channel performs the final log rendering and the dispatching on the underlying media on a separate thread, so the operation of generating a log for a certain channel is virtually non-blocking and relatively fast.
Log operations involving fixed parameters are nearly no-ops, as in the following example:
glog( 2000, "A very low priority level, unlikely to be ever logged" )
Although there is a cost in calling the glog function (logging on the GenericLog area), a modern computer can perform such calls in the order of about five millions per second.
However, consider the nature of falcon as a Virtual Machine interpreted scripting language. Creating a complete log message may be an heavy operation by itself; for example:
rendered = "" for person in visitors rendered += person formiddle: rendered += ", " end GenericLog.log( LOGI, "Today we had " + visitors.len() + " visitors, named " + rendered )
In this case, creating the "rendered" log-friendly representation of the visitors is quite slow, and so it's creating the log entry in the log() call.
If there isn't any channel registered with the GenericLog area, the message will be discarded, but the heaviest part of the job has already be done, and in waste.
In case logs are heavy and frequent, it is useful to wrap log generation of the most intensive entries in runtime checks, or better, compile time directives that prevent calling logs when the application knows they are actually not wanted in the current context.
The LogArea.minlog method returns the minimum log level that is accepted by a registered channel (the gminlog function operates on the GeneralLog area), providing a simple way to prevent logging useless data in runtime:
// want debug? if LOGD <= gminlog() // prepare an heavy log... glogd( "Debug heavy log: " + ... ) end
Note: Log levels appares in inverse order of severity; so LOGF (fatal) is a level numerically less than LOGD (debug).
Falcon logging module is exposed to embedding application as a "service". This means that embedding applications using the Falcon engine can dynamically load the logging module and access the same functionalities available to Falcon scripts directly from a C++ SDK interface.
Actually, LogChannel class hierarchy is directly visible in the scripts; so embedding application could create log areas or ever log channels pointing back to them, or sharing channels with the scripts to write joint logs (maybe from different areas).
All the classes in this module are highly reflective, and operate on the inner C++ classes for performance reason. Because of this, overloading a LogChannel class and overloading its log method won't cause the LogArea to call back the new log method provided by the script; the underlying C++ log method will still be called.
However, it is legal to extend the LogChannel classes; the overloaded log method can be used directly by the script even if it will be ignored by LogArea instances.
LogArea | Collection of log channels. |
LogChannel | Abstract class receiving log requests from log areas. |
LogChannelFiles | Log channel sending logs to a set of (possibly) rotating local files. |
LogChannelStream | Logs on an open stream. |
LogChannelSyslog | Logs on the local system logger facility. |
glog | Shortcut to log on the generic area. |
glogd | Shortcut to log a debug message on the generic area. |
gloge | Shortcut to log an error on the generic area. |
glogf | Shortcut to log a fatal error on the generic area. |
glogi | Shortcut to log an information message on the generic area. |
glogw | Shortcut to log a warning on the generic area. |
gminlog | Determines what is the minimum log severity active on the GeneircLog area. |