This module implements a simple logger.
It has been designed to be as simple as possible to avoid bloat. If this library does not fulfill your needs, write your own.
Basic usage
To get started, first create a logger:
import logging var logger = newConsoleLogger()
The logger that was created above logs to the console, but this module also provides loggers that log to files, such as the FileLogger. Creating custom loggers is also possible by inheriting from the Logger type.
Once a logger has been created, call its log proc to log a message:
logger.log(lvlInfo, "a log message") # Output: INFO a log message
The INFO within the output is the result of a format string being prepended to the message, and it will differ depending on the message's level. Format strings are explained in more detail here.
There are six logging levels: debug, info, notice, warn, error, and fatal. They are described in more detail within the Level enum's documentation. A message is logged if its level is at or above both the logger's levelThreshold field and the global log filter. The latter can be changed with the setLogFilter proc.
Warning:
- For loggers that log to a console or to files, only error and fatal messages will cause their output buffers to be flushed immediately. Use the flushFile proc to flush the buffer manually if needed.
Handlers
When using multiple loggers, calling the log proc for each logger can become repetitive. Instead of doing that, register each logger that will be used with the addHandler proc, which is demonstrated in the following example:
import logging var consoleLog = newConsoleLogger() var fileLog = newFileLogger("errors.log", levelThreshold=lvlError) var rollingLog = newRollingFileLogger("rolling.log") addHandler(consoleLog) addHandler(fileLog) addHandler(rollingLog)
After doing this, use either the log template or one of the level-specific templates, such as the error template, to log messages to all registered handlers at once.
# This example uses the loggers created above log(lvlError, "an error occurred") error("an error occurred") # Equivalent to the above line info("something normal happened") # Will not be written to errors.log
Note that a message's level is still checked against each handler's levelThreshold and the global log filter.
Format strings
Log messages are prefixed with format strings. These strings contain placeholders for variables, such as $time, that are replaced with their corresponding values, such as the current time, before they are prepended to a log message. Characters that are not part of variables are unaffected.
The format string used by a logger can be specified by providing the fmtStr argument when creating the logger or by setting its fmtStr field afterward. If not specified, the default format string is used.
The following variables, which must be prefixed with a dollar sign ($), are available:
Variable | Output |
---|---|
$date | Current date |
$time | Current time |
$datetime | $dateT$time |
$app | os.getAppFilename() |
$appname | Base name of $app |
$appdir | Directory name of $app |
$levelid | First letter of log level |
$levelname | Log level name |
Note that $app, $appname, and $appdir are not supported when using the JavaScript backend.
The following example illustrates how to use format strings:
import logging var logger = newConsoleLogger(fmtStr="[$time] - $levelname: ") logger.log(lvlInfo, "this is a message") # Output: [19:50:13] - INFO: this is a message
Notes when using multiple threads
There are a few details to keep in mind when using this module within multiple threads:
- The global log filter is actually a thread-local variable, so it needs to be set in each thread that uses this module.
- The list of registered handlers is also a thread-local variable. If a handler will be used in multiple threads, it needs to be registered in each of those threads.
See also
- strutils module for common string functions
- strformat module for string interpolation and formatting
- strscans module for scanf and scanp macros, which offer easier substring extraction than regular expressions
Types
Level = enum lvlAll, lvlDebug, lvlInfo, lvlNotice, lvlWarn, lvlError, lvlFatal, lvlNone
-
Enumeration of logging levels.
Debug messages represent the lowest logging level, and fatal error messages represent the highest logging level. lvlAll can be used to enable all messages, while lvlNone can be used to disable all messages.
Typical usage for each logging level, from lowest to highest, is described below:
- Debug - debugging information helpful only to developers
- Info - anything associated with normal operation and without any particular importance
- Notice - more important information that users should be notified about
- Warn - impending problems that require some attention
- Error - error conditions that the application can recover from
- Fatal - fatal errors that prevent the application from continuing
It is completely up to the application how to utilize each level.
Individual loggers have a levelThreshold field that filters out any messages with a level lower than the threshold. There is also a global filter that applies to all log messages, and it can be changed using the setLogFilter proc.
Source Edit Logger = ref object of RootObj levelThreshold*: Level ## Only messages that are at or above this ## threshold will be logged fmtStr*: string ## Format string to prepend to each log message; ## defaultFmtStr is the default
-
The abstract base type of all loggers.
Custom loggers should inherit from this type. They should also provide their own implementation of the log method.
See also:
Source Edit ConsoleLogger = ref object of Logger useStderr*: bool ## If true, writes to stderr; otherwise, writes to stdout
-
A logger that writes log messages to the console.
Create a new ConsoleLogger with the newConsoleLogger proc.
See also:
Source Edit FileLogger = ref object of Logger file*: File ## The wrapped file
-
A logger that writes log messages to a file.
Create a new FileLogger with the newFileLogger proc.
Note: This logger is not available for the JavaScript backend.
See also:
Source Edit RollingFileLogger = ref object of FileLogger maxLines: int curLine: int baseName: string baseMode: FileMode logFiles: int bufSize: int
-
A logger that writes log messages to a file while performing log rotation.
Create a new RollingFileLogger with the newRollingFileLogger proc.
Note: This logger is not available for the JavaScript backend.
See also:
Source Edit
Consts
LevelNames: array[Level, string] = ["DEBUG", "DEBUG", "INFO", "NOTICE", "WARN", "ERROR", "FATAL", "NONE"]
- Array of strings representing each logging level. Source Edit
defaultFmtStr = "$levelname "
- The default format string. Source Edit
verboseFmtStr = "$levelid, [$datetime] -- $appname: "
-
A more verbose format string.
This string can be passed as the frmStr argument to procs that create new loggers, such as the newConsoleLogger proc.
If a different format string is preferred, refer to the documentation about format strings for more information, including a list of available variables.
Source Edit
Procs
proc substituteLog(frmt: string; level: Level; args: varargs[string, `$`]): string {...}{. raises: [], tags: [ReadIOEffect, TimeEffect].}
-
Formats a log message at the specified level with the given format string.
The format variables present within frmt will be replaced with the corresponding values before being prepended to args and returned.
Unless you are implementing a custom logger, there is little need to call this directly. Use either a logger's log method or one of the logging templates.
See also:
- log method for the ConsoleLogger
- log method for the FileLogger
- log method for the RollingFileLogger
- log template
Example:
doAssert substituteLog(defaultFmtStr, lvlInfo, "a message") == "INFO a message" doAssert substituteLog("$levelid - ", lvlError, "an error") == "E - an error" doAssert substituteLog("$levelid", lvlDebug, "error") == "Derror"
Source Edit proc newConsoleLogger(levelThreshold = lvlAll; fmtStr = defaultFmtStr; useStderr = false): ConsoleLogger {...}{.raises: [], tags: [].}
-
Creates a new ConsoleLogger.
By default, log messages are written to stdout. If useStderr is true, they are written to stderr instead.
For the JavaScript backend, log messages are written to the console, and useStderr is ignored.
See also:
- newFileLogger proc that uses a file handle
- newFileLogger proc that accepts a filename
- newRollingFileLogger proc
Examples:
var normalLog = newConsoleLogger() var formatLog = newConsoleLogger(fmtStr=verboseFmtStr) var errorLog = newConsoleLogger(levelThreshold=lvlError, useStderr=true)
Source Edit proc defaultFilename(): string {...}{.raises: [], tags: [ReadIOEffect].}
-
Returns the filename that is used by default when naming log files.
Note: This proc is not available for the JavaScript backend.
Source Edit proc newFileLogger(file: File; levelThreshold = lvlAll; fmtStr = defaultFmtStr): FileLogger {...}{. raises: [], tags: [].}
-
Creates a new FileLogger that uses the given file handle.
Note: This proc is not available for the JavaScript backend.
See also:
- newConsoleLogger proc
- newFileLogger proc that accepts a filename
- newRollingFileLogger proc
Examples:
var messages = open("messages.log", fmWrite) var formatted = open("formatted.log", fmWrite) var errors = open("errors.log", fmWrite) var normalLog = newFileLogger(messages) var formatLog = newFileLogger(formatted, fmtStr=verboseFmtStr) var errorLog = newFileLogger(errors, levelThreshold=lvlError)
Source Edit proc newFileLogger(filename = defaultFilename(); mode: FileMode = fmAppend; levelThreshold = lvlAll; fmtStr = defaultFmtStr; bufSize: int = -1): FileLogger {...}{.raises: [IOError], tags: [].}
-
Creates a new FileLogger that logs to a file with the given filename.
bufSize controls the size of the output buffer that is used when writing to the log file. The following values can be provided:
- -1 - use system defaults
- 0 - unbuffered
- > 0 - fixed buffer size
Note: This proc is not available for the JavaScript backend.
See also:
- newConsoleLogger proc
- newFileLogger proc that uses a file handle
- newRollingFileLogger proc
Examples:
var normalLog = newFileLogger("messages.log") var formatLog = newFileLogger("formatted.log", fmtStr=verboseFmtStr) var errorLog = newFileLogger("errors.log", levelThreshold=lvlError)
Source Edit proc newRollingFileLogger(filename = defaultFilename(); mode: FileMode = fmReadWrite; levelThreshold = lvlAll; fmtStr = defaultFmtStr; maxLines: Positive = 1000; bufSize: int = -1): RollingFileLogger {...}{. raises: [IOError, OSError], tags: [ReadDirEffect, ReadIOEffect].}
-
Creates a new RollingFileLogger.
Once the current log file being written to contains maxLines lines, a new log file will be created, and the old log file will be renamed.
bufSize controls the size of the output buffer that is used when writing to the log file. The following values can be provided:
- -1 - use system defaults
- 0 - unbuffered
- > 0 - fixed buffer size
Note: This proc is not available in the JavaScript backend.
See also:
- newConsoleLogger proc
- newFileLogger proc that uses a file handle
- newFileLogger proc that accepts a filename
Examples:
var normalLog = newRollingFileLogger("messages.log") var formatLog = newRollingFileLogger("formatted.log", fmtStr=verboseFmtStr) var shortLog = newRollingFileLogger("short.log", maxLines=200) var errorLog = newRollingFileLogger("errors.log", levelThreshold=lvlError)
Source Edit proc addHandler(handler: Logger) {...}{.raises: [], tags: [].}
-
Adds a logger to the list of registered handlers.
Warning: The list of handlers is a thread-local variable. If the given handler will be used in multiple threads, this proc should be called in each of those threads.
See also:
Example:
var logger = newConsoleLogger() addHandler(logger) doAssert logger in getHandlers()
Source Edit proc getHandlers(): seq[Logger] {...}{.raises: [], tags: [].}
-
Returns a list of all the registered handlers.
See also:
Source Edit proc setLogFilter(lvl: Level) {...}{.raises: [], tags: [].}
-
Sets the global log filter.
Messages below the provided level will not be logged regardless of an individual logger's levelThreshold. By default, all messages are logged.
Warning: The global log filter is a thread-local variable. If logging is being performed in multiple threads, this proc should be called in each thread unless it is intended that different threads should log at different logging levels.
See also:
Example:
setLogFilter(lvlError) doAssert getLogFilter() == lvlError
Source Edit proc getLogFilter(): Level {...}{.raises: [], tags: [].}
-
Gets the global log filter.
See also:
Source Edit
Methods
method log(logger: Logger; level: Level; args: varargs[string, `$`]) {...}{. raises: [Exception], gcsafe, tags: [RootEffect], base.}
-
Override this method in custom loggers. The default implementation does nothing.
See also:
- log method for the ConsoleLogger
- log method for the FileLogger
- log method for the RollingFileLogger
- log template
method log(logger: ConsoleLogger; level: Level; args: varargs[string, `$`]) {...}{. raises: [], tags: [ReadIOEffect, TimeEffect, WriteIOEffect].}
-
Logs to the console with the given ConsoleLogger only.
This method ignores the list of registered handlers.
Whether the message is logged depends on both the ConsoleLogger's levelThreshold field and the global log filter set using the setLogFilter proc.
Note: Only error and fatal messages will cause the output buffer to be flushed immediately. Use the flushFile proc to flush the buffer manually if needed.
See also:
- log method for the FileLogger
- log method for the RollingFileLogger
- log template
Examples:
var consoleLog = newConsoleLogger() consoleLog.log(lvlInfo, "this is a message") consoleLog.log(lvlError, "error code is: ", 404)
Source Edit method log(logger: FileLogger; level: Level; args: varargs[string, `$`]) {...}{. raises: [IOError], tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
-
Logs a message at the specified level using the given FileLogger only.
This method ignores the list of registered handlers.
Whether the message is logged depends on both the FileLogger's levelThreshold field and the global log filter set using the setLogFilter proc.
Notes:
- Only error and fatal messages will cause the output buffer to be flushed immediately. Use the flushFile proc to flush the buffer manually if needed.
- This method is not available for the JavaScript backend.
See also:
- log method for the ConsoleLogger
- log method for the RollingFileLogger
- log template
Examples:
var fileLog = newFileLogger("messages.log") fileLog.log(lvlInfo, "this is a message") fileLog.log(lvlError, "error code is: ", 404)
Source Edit method log(logger: RollingFileLogger; level: Level; args: varargs[string, `$`]) {...}{. raises: [OSError, IOError], tags: [ReadIOEffect, WriteIOEffect, TimeEffect].}
-
Logs a message at the specified level using the given RollingFileLogger only.
This method ignores the list of registered handlers.
Whether the message is logged depends on both the RollingFileLogger's levelThreshold field and the global log filter set using the setLogFilter proc.
Notes:
- Only error and fatal messages will cause the output buffer to be flushed immediately. Use the flushFile proc to flush the buffer manually if needed.
- This method is not available for the JavaScript backend.
See also:
- log method for the ConsoleLogger
- log method for the FileLogger
- log template
Examples:
var rollingLog = newRollingFileLogger("messages.log") rollingLog.log(lvlInfo, "this is a message") rollingLog.log(lvlError, "error code is: ", 404)
Source Edit
Templates
template log(level: Level; args: varargs[string, `$`])
-
Logs a message at the specified level to all registered handlers.
Whether the message is logged depends on both the FileLogger's levelThreshold field and the global log filter set using the setLogFilter proc.
Examples:
var logger = newConsoleLogger() addHandler(logger) log(lvlInfo, "This is an example.")
See also:
Source Edit template debug(args: varargs[string, `$`])
-
Logs a debug message to all registered handlers.
Debug messages are typically useful to the application developer only, and they are usually disabled in release builds, although this template does not make that distinction.
Examples:
var logger = newConsoleLogger() addHandler(logger) debug("myProc called with arguments: foo, 5")
See also:
Source Edit template info(args: varargs[string, `$`])
-
Logs an info message to all registered handlers.
Info messages are typically generated during the normal operation of an application and are of no particular importance. It can be useful to aggregate these messages for later analysis.
Examples:
var logger = newConsoleLogger() addHandler(logger) info("Application started successfully.")
See also:
Source Edit template notice(args: varargs[string, `$`])
-
Logs an notice to all registered handlers.
Notices are semantically very similar to info messages, but they are meant to be messages that the user should be actively notified about, depending on the application.
Examples:
var logger = newConsoleLogger() addHandler(logger) notice("An important operation has completed.")
See also:
Source Edit template warn(args: varargs[string, `$`])
-
Logs a warning message to all registered handlers.
A warning is a non-error message that may indicate impending problems or degraded performance.
Examples:
var logger = newConsoleLogger() addHandler(logger) warn("The previous operation took too long to process.")
See also:
Source Edit template error(args: varargs[string, `$`])
-
Logs an error message to all registered handlers.
Error messages are for application-level error conditions, such as when some user input generated an exception. Typically, the application will continue to run, but with degraded functionality or loss of data, and these effects might be visible to users.
Examples:
var logger = newConsoleLogger() addHandler(logger) error("An exception occurred while processing the form.")
See also:
Source Edit template fatal(args: varargs[string, `$`])
-
Logs a fatal error message to all registered handlers.
Fatal error messages usually indicate that the application cannot continue to run and will exit due to a fatal condition. This template only logs the message, and it is the application's responsibility to exit properly.
Examples:
var logger = newConsoleLogger() addHandler(logger) fatal("Can't open database -- exiting.")
See also:
Source Edit