It decides whether to output the messages, and if so, where to. Right now, in practice this is a singleton class.
The system is distantly inspired by log4j, at http://jakarta.apache.org/log4j/
While our implementation is based on Logger instances, in practice we consider it unreasonable to expect programmers to explicitly instantiate them. So instead, we expect that in general the only interaction with this module will be via these global functions:
bu_debug(arg0, ...) bu_info(arg0, ...) bu_warn(arg0, ...) bu_error(arg0, ...)
bu_alert(message)
Loggers
There are a set of burst.logging.Log instances, each with a name. Any instance can have a parent. By convention, a "dotted name" is used for names, which reflects this containment hierarchy, so that a child would have the name "parentname.childname".
There is always at least the "root logger" instance (held in global bu_Log). There are no others unless client code creates them. Note that to strictly match the dotted name convention, the root logger should have the name "" (empty string). Instead it is given the name 'root'.
Each burst.logging.Log instance contains this state:
When log messages are produced by the application, it is with a particular verbosity level (from lowest, FATAL, to highest, DEBUG). Any log instance can have configured what the highest level is that it will respond to. This might come from an external configuration, associated with the logger name. If its maximum level is not configured, it will inherit from its parent, bubbling up until it reaches the root logger, which always has a maximum level configured (out of the box, at the WARN level). If the logger instance determines that the message verbosity level is less than or equal to its (possibly inherited) maximum level, then the message is said to be "enabled"; otherwise it is "disabled".
There can be zero, one, or more "appenders" associated with any burst.logging.Log instance. If a message is "disabled", then it is not sent to any appenders. If a message is "enabled", then the burst.logging.Log instance sends it to all of its appenders, and to all the appenders up its inheritance hierarchy. For this reason, typically appenders are associated only with the root logger. (Log4j supports an "additivity flag" at any logger which stops bubbling up to parent appenders. We have not implemented that feature.)
Note that appenders are associated with logger instances in the logger hierarchy, not with a particular verbosity level. Of course any appender implementation may choose on its own to not respond to some message levels.
The format of logged lines can be controlled by supplying a custom formatter, which would then be used when sending to all appenders. The default format is:
time level [logger] message
The format method is called as: format(logger, level, message, stack_start)
The stack_start parameter is the number of stack frames to skip to get to the actual caller. It is available if the formatter wants to carry out (potentially expensive) investigation of the stack to determine caller class, caller method, file name, and/or line number.
(Fyi, log4j has special format strings, see http://jakarta.apache.org/log4j/docs/api/org/apache/log4j/PatternLayout.html )
This class has several class properties, including: 'maxLevel', 'appender', 'formatter'.
As with any module in this library, application-specific configuration of class properties is done after the entire library is loaded. That leaves the question of what to do with calls into this module while the library itself is being loaded. This is something of a catch-22 challenge, because the functionality to do class property configuration may not even be defined at the time of a call.
So we have to have a temporary fixed behavior that governs what happens during the library load phase. In a shell, we use an appender which uses the shell's println functionality (this is governed by BU_LOG_USE_PRINTLN_APPENDER). In a browser, we use an appender which holds all messages in a circular Array buffer (circular, so it doesn't grow without bound). If an appender is added in the configuration phase, any accumulated messages in the circular Array is flushed to the new appender.
At configuration time, the application may set any appender it likes via the appender class property. If specified, it will replace any appenders that already exist (and, in the case of the circular buffer appender, flush its pending messages to the new appender).
We provide one potentially useful burst.logging.Appender subclass, called burst.logging.AppenderIframe, which sends its output to an iframe within the current web page. (This is not useful in a shell, obviously.) This appender is lazy, in that it does not add the iframe child to the current page until there is actually some output to display (which there may not be, depending on maxLevel). This would be configured for example by:
var bu_AppConfig = {'burst.logging.Log.appender' : 'new burst.logging.AppenderIframe()'};Any of the optional constructor parameters could of course be specified; the whole string value is eval'd at configuration time to determine the burst.logging.Appender instance.The global variable burst.logging.Log.ROOT_MAX_LEVEL governs the debug level that is used in the period between when this Log.js file is loaded, and when the external configuration (if any) is applied. You may set that variable prior to loading the file by something like this:
burst.logging.Log.ROOT_MAX_LEVEL = 'DEBUG';If it is not set at configuration time, it keeps whatever level is set at load time via that variable (WARN by default).If no configuration at all is done:
For any output to be seen it is necessary configure both an appender and set the debug level appropriately.
(Note that there is also the catch-22 problem of how library maintainers debug the module itself, but we don't get into that here. See the function bu_dbgdbg_
in the source.)
incorporate any ideas/code from http://code.audiofarm.de/Logger/ (written in ActionScript 2.0)
maybe let bu_debug and so on be used in burst_first after all
Public Member Functions | |
Log (String name, burst.logging.Log parent) | |
Constructor for a burst.logging.Log. | |
void | debug (...) |
Issue a debug message. | |
void | setMaxLevel (Object level) |
Sets the highest level to pass on to any appender. | |
Boolean | isOn (Object level) |
Whether the specified level is passed by this instance. | |
void | addAppender (Object appender) |
Add an appender to this instance. | |
void | setAppender (Object appender, Boolean copy_buffered) |
Will clear any existing appenders, and then add the given one. | |
Static Public Member Functions | |
Log | getLogger (String name) |
Get a logger instance by name, or null if none. | |
void | enableDebug (Boolean isEnabled) |
Will enable or disable debug at a global level (this is a class method). | |
Object | toLevelObject (Object level) |
Convert a String name or Number level to a BU_LogLevel object. | |
String | format (BU_Log logger, BU_LogLevel levelobj, String message, Number stack_start) |
The class method called to format a line of text. | |
void | setFormatter (Function func) |
Set the formatter. | |
Static Public Attributes | |
final BU_LogLevel | DEBUG = new BU_LogLevel(DEBUG, 7) |
The debug level. | |
final BU_LogLevel | INFO = new BU_LogLevel('INFO', 6) |
The info level. | |
final BU_LogLevel | WARN = new BU_LogLevel('WARN', 4) |
The warn level. | |
final BU_LogLevel | ERROR = new BU_LogLevel(ERROR, 3) |
The error level. | |
final BU_LogLevel | FATAL = new BU_LogLevel(FATAL, 0) |
The fatal level. | |
String | maxLevel |
Class property: The max level of the "root" logger. | |
Expr | appender |
Class property: If specified, the string is eval'd and the result is passed to setAppender. | |
Expr | formatter |
Class property: If specified, the string is eval'd and the result is passed to burst.logging.Log.setFormatter. |
|
Constructor for a burst.logging.Log. No args for the root instance.
|
|
Add an appender to this instance. Currently there is no protection against duplicate adds.
|
|
Issue a debug message.
Same as |
|
Will enable or disable debug at a global level (this is a class method).
This is efficiently redefining the Note that if it is disabled, no debug will at all will ever come out, regardless of the debug level set in any BU_Log instance. If it is enabled, any filtering at the instance level still applies.
If
|
|
Get a logger instance by name, or null if none.
|
|
Whether the specified level is passed by this instance. If max_level_ is set, it does a comparison, otherwise bubbles to parent.
|
|
Will clear any existing appenders, and then add the given one. Optionally, will also copy over any buffered log statements from a previous burst.logging.AppenderBuffer.
|
|
Set the formatter. Must implement signature of BU_Log.format. This oly has an affect on appenders which do not do their own formatting. |
|
Sets the highest level to pass on to any appender. If null or undefined is passed in, it will cause this instance to inherit from its parent.
|
|
Convert a String name or Number level to a BU_LogLevel object. If it is already a BU_LogLevel instance, it is just returned unchanged. |
|
Class property: If specified, the string is eval'd and the result is passed to setAppender. No default. |
|
The fatal level. This differs from ERROR in that messages to this level automatically cause an exception. |
|
Class property: If specified, the string is eval'd and the result is passed to burst.logging.Log.setFormatter. No default. |
|
Class property: The max level of the "root" logger. A string name of a level such as "DEBUG". If specified, it overrides BU_LOG_ROOT_MAX_LEVEL. No default. |