123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908 |
- #
- #
- # Nim's Runtime Library
- # (c) Copyright 2015 Andreas Rumpf, Dominik Picheta
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- ## 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:
- ##
- ## ```Nim
- ## import std/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<#FileLogger>`_. Creating custom loggers is also possible by
- ## inheriting from the `Logger<#Logger>`_ type.
- ##
- ## Once a logger has been created, call its `log proc
- ## <#log.e,ConsoleLogger,Level,varargs[string,]>`_ to log a message:
- ##
- ## ```Nim
- ## 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<#basic-usage-format-strings>`_.
- ##
- ## There are six logging levels: debug, info, notice, warn, error, and fatal.
- ## They are described in more detail within the `Level enum's documentation
- ## <#Level>`_. 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<#setLogFilter,Level>`_.
- ##
- ## .. 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 by default.
- ## set ``flushThreshold`` when creating the logger to change this.
- ##
- ## 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<#addHandler,Logger>`_, which is demonstrated
- ## in the following example:
- ##
- ## ```Nim
- ## import std/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
- ## <#log.t,Level,varargs[string,]>`_ or one of the level-specific templates,
- ## such as the `error template<#error.t,varargs[string,]>`_, to log messages
- ## to all registered handlers at once.
- ##
- ## ```Nim
- ## # 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<#defaultFmtStr>`_ 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()<os.html#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:
- ##
- ## ```Nim
- ## import std/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<strutils.html>`_ for common string functions
- ## * `strformat module<strformat.html>`_ for string interpolation and formatting
- ## * `strscans module<strscans.html>`_ for ``scanf`` and ``scanp`` macros, which
- ## offer easier substring extraction than regular expressions
- import std/[strutils, times]
- when not defined(js):
- import std/os
- when defined(nimPreviewSlimSystem):
- import std/syncio
- type
- Level* = enum ## \
- ## 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<#setLogFilter,Level>`_.
- lvlAll, ## All levels active
- lvlDebug, ## Debug level and above are active
- lvlInfo, ## Info level and above are active
- lvlNotice, ## Notice level and above are active
- lvlWarn, ## Warn level and above are active
- lvlError, ## Error level and above are active
- lvlFatal, ## Fatal level and above are active
- lvlNone ## No levels active; nothing is logged
- const
- LevelNames*: array[Level, string] = [
- "DEBUG", "DEBUG", "INFO", "NOTICE", "WARN", "ERROR", "FATAL", "NONE"
- ] ## Array of strings representing each logging level.
- defaultFmtStr* = "$levelname " ## The default format string.
- 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<#newConsoleLogger>`_.
- ##
- ## If a different format string is preferred, refer to the
- ## `documentation about format strings<#basic-usage-format-strings>`_
- ## for more information, including a list of available variables.
- defaultFlushThreshold = when NimMajor >= 2:
- when defined(nimV1LogFlushBehavior): lvlError else: lvlAll
- else:
- when defined(nimFlushAllLogs): lvlAll else: lvlError
- ## The threshold above which log messages to file-like loggers
- ## are automatically flushed.
- ##
- ## By default, only error and fatal messages are logged,
- ## but defining ``-d:nimFlushAllLogs`` will make all levels be flushed
- type
- Logger* = ref object of RootObj
- ## 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<#log.e,Logger,Level,varargs[string,]>`_.
- ##
- ## See also:
- ## * `ConsoleLogger<#ConsoleLogger>`_
- ## * `FileLogger<#FileLogger>`_
- ## * `RollingFileLogger<#RollingFileLogger>`_
- 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
- ConsoleLogger* = ref object of Logger
- ## A logger that writes log messages to the console.
- ##
- ## Create a new ``ConsoleLogger`` with the `newConsoleLogger proc
- ## <#newConsoleLogger>`_.
- ##
- ## See also:
- ## * `FileLogger<#FileLogger>`_
- ## * `RollingFileLogger<#RollingFileLogger>`_
- useStderr*: bool ## If true, writes to stderr; otherwise, writes to stdout
- flushThreshold*: Level ## Only messages that are at or above this
- ## threshold will be flushed immediately
- when not defined(js):
- type
- FileLogger* = ref object of Logger
- ## A logger that writes log messages to a file.
- ##
- ## Create a new ``FileLogger`` with the `newFileLogger proc
- ## <#newFileLogger,File>`_.
- ##
- ## **Note:** This logger is not available for the JavaScript backend.
- ##
- ## See also:
- ## * `ConsoleLogger<#ConsoleLogger>`_
- ## * `RollingFileLogger<#RollingFileLogger>`_
- file*: File ## The wrapped file
- flushThreshold*: Level ## Only messages that are at or above this
- ## threshold will be flushed immediately
- RollingFileLogger* = ref object of FileLogger
- ## A logger that writes log messages to a file while performing log
- ## rotation.
- ##
- ## Create a new ``RollingFileLogger`` with the `newRollingFileLogger proc
- ## <#newRollingFileLogger,FileMode,Positive,int>`_.
- ##
- ## **Note:** This logger is not available for the JavaScript backend.
- ##
- ## See also:
- ## * `ConsoleLogger<#ConsoleLogger>`_
- ## * `FileLogger<#FileLogger>`_
- maxLines: int # maximum number of lines
- curLine: int
- baseName: string # initial filename
- baseMode: FileMode # initial file mode
- logFiles: int # how many log files already created, e.g. basename.1, basename.2...
- bufSize: int # size of output buffer (-1: use system defaults, 0: unbuffered, >0: fixed buffer size)
- var
- level {.threadvar.}: Level ## global log filter
- handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels
- proc substituteLog*(frmt: string, level: Level,
- args: varargs[string, `$`]): string =
- ## Formats a log message at the specified level with the given format string.
- ##
- ## The `format variables<#basic-usage-format-strings>`_ 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<#log.e,ConsoleLogger,Level,varargs[string,]>`_
- ## for the ConsoleLogger
- ## * `log method<#log.e,FileLogger,Level,varargs[string,]>`_
- ## for the FileLogger
- ## * `log method<#log.e,RollingFileLogger,Level,varargs[string,]>`_
- ## for the RollingFileLogger
- ## * `log template<#log.t,Level,varargs[string,]>`_
- runnableExamples:
- doAssert substituteLog(defaultFmtStr, lvlInfo, "a message") == "INFO a message"
- doAssert substituteLog("$levelid - ", lvlError, "an error") == "E - an error"
- doAssert substituteLog("$levelid", lvlDebug, "error") == "Derror"
- var msgLen = 0
- for arg in args:
- msgLen += arg.len
- result = newStringOfCap(frmt.len + msgLen + 20)
- var i = 0
- while i < frmt.len:
- if frmt[i] != '$':
- result.add(frmt[i])
- inc(i)
- else:
- inc(i)
- var v = ""
- let app = when defined(js): "" else: getAppFilename()
- while i < frmt.len and frmt[i] in IdentChars:
- v.add(toLowerAscii(frmt[i]))
- inc(i)
- case v
- of "date": result.add(getDateStr())
- of "time": result.add(getClockStr())
- of "datetime": result.add(getDateStr() & "T" & getClockStr())
- of "app": result.add(app)
- of "appdir":
- when not defined(js): result.add(app.splitFile.dir)
- of "appname":
- when not defined(js): result.add(app.splitFile.name)
- of "levelid": result.add(LevelNames[level][0])
- of "levelname": result.add(LevelNames[level])
- else: discard
- for arg in args:
- result.add(arg)
- 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<#log.e,ConsoleLogger,Level,varargs[string,]>`_
- ## for the ConsoleLogger
- ## * `log method<#log.e,FileLogger,Level,varargs[string,]>`_
- ## for the FileLogger
- ## * `log method<#log.e,RollingFileLogger,Level,varargs[string,]>`_
- ## for the RollingFileLogger
- ## * `log template<#log.t,Level,varargs[string,]>`_
- discard
- method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) =
- ## Logs to the console with the given `ConsoleLogger<#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<#setLogFilter,Level>`_.
- ##
- ## **Note:** Only error and fatal messages will cause the output buffer
- ## to be flushed immediately by default. Set ``flushThreshold`` when creating
- ## the logger to change this.
- ##
- ## See also:
- ## * `log method<#log.e,FileLogger,Level,varargs[string,]>`_
- ## for the FileLogger
- ## * `log method<#log.e,RollingFileLogger,Level,varargs[string,]>`_
- ## for the RollingFileLogger
- ## * `log template<#log.t,Level,varargs[string,]>`_
- ##
- ## **Examples:**
- ##
- ## ```Nim
- ## var consoleLog = newConsoleLogger()
- ## consoleLog.log(lvlInfo, "this is a message")
- ## consoleLog.log(lvlError, "error code is: ", 404)
- ## ```
- if level >= logging.level and level >= logger.levelThreshold:
- let ln = substituteLog(logger.fmtStr, level, args)
- when defined(js):
- let cln = ln.cstring
- case level
- of lvlDebug: {.emit: "console.debug(`cln`);".}
- of lvlInfo: {.emit: "console.info(`cln`);".}
- of lvlWarn: {.emit: "console.warn(`cln`);".}
- of lvlError: {.emit: "console.error(`cln`);".}
- else: {.emit: "console.log(`cln`);".}
- else:
- try:
- var handle = stdout
- if logger.useStderr:
- handle = stderr
- writeLine(handle, ln)
- if level >= logger.flushThreshold: flushFile(handle)
- except IOError:
- discard
- proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr,
- useStderr = false, flushThreshold = defaultFlushThreshold): ConsoleLogger =
- ## Creates a new `ConsoleLogger<#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<#newFileLogger,File>`_ that uses a file handle
- ## * `newFileLogger proc<#newFileLogger,FileMode,int>`_
- ## that accepts a filename
- ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,Positive,int>`_
- ##
- ## **Examples:**
- ##
- ## ```Nim
- ## var normalLog = newConsoleLogger()
- ## var formatLog = newConsoleLogger(fmtStr=verboseFmtStr)
- ## var errorLog = newConsoleLogger(levelThreshold=lvlError, useStderr=true)
- ## ```
- new result
- result.fmtStr = fmtStr
- result.levelThreshold = levelThreshold
- result.flushThreshold = flushThreshold
- result.useStderr = useStderr
- when not defined(js):
- method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) =
- ## Logs a message at the specified level using the given
- ## `FileLogger<#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<#setLogFilter,Level>`_.
- ##
- ## **Notes:**
- ## * Only error and fatal messages will cause the output buffer
- ## to be flushed immediately by default. Set ``flushThreshold`` when creating
- ## the logger to change this.
- ## * This method is not available for the JavaScript backend.
- ##
- ## See also:
- ## * `log method<#log.e,ConsoleLogger,Level,varargs[string,]>`_
- ## for the ConsoleLogger
- ## * `log method<#log.e,RollingFileLogger,Level,varargs[string,]>`_
- ## for the RollingFileLogger
- ## * `log template<#log.t,Level,varargs[string,]>`_
- ##
- ## **Examples:**
- ##
- ## ```Nim
- ## var fileLog = newFileLogger("messages.log")
- ## fileLog.log(lvlInfo, "this is a message")
- ## fileLog.log(lvlError, "error code is: ", 404)
- ## ```
- if level >= logging.level and level >= logger.levelThreshold:
- writeLine(logger.file, substituteLog(logger.fmtStr, level, args))
- if level >= logger.flushThreshold: flushFile(logger.file)
- proc defaultFilename*(): string =
- ## Returns the filename that is used by default when naming log files.
- ##
- ## **Note:** This proc is not available for the JavaScript backend.
- var (path, name, _) = splitFile(getAppFilename())
- result = changeFileExt(path / name, "log")
- proc newFileLogger*(file: File,
- levelThreshold = lvlAll,
- fmtStr = defaultFmtStr,
- flushThreshold = defaultFlushThreshold): FileLogger =
- ## Creates a new `FileLogger<#FileLogger>`_ that uses the given file handle.
- ##
- ## **Note:** This proc is not available for the JavaScript backend.
- ##
- ## See also:
- ## * `newConsoleLogger proc<#newConsoleLogger>`_
- ## * `newFileLogger proc<#newFileLogger,FileMode,int>`_
- ## that accepts a filename
- ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,Positive,int>`_
- ##
- ## **Examples:**
- ##
- ## ```Nim
- ## 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)
- ## ```
- new(result)
- result.file = file
- result.levelThreshold = levelThreshold
- result.flushThreshold = flushThreshold
- result.fmtStr = fmtStr
- proc newFileLogger*(filename = defaultFilename(),
- mode: FileMode = fmAppend,
- levelThreshold = lvlAll,
- fmtStr = defaultFmtStr,
- bufSize: int = -1,
- flushThreshold = defaultFlushThreshold): FileLogger =
- ## Creates a new `FileLogger<#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<#newConsoleLogger>`_
- ## * `newFileLogger proc<#newFileLogger,File>`_ that uses a file handle
- ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,Positive,int>`_
- ##
- ## **Examples:**
- ##
- ## ```Nim
- ## var normalLog = newFileLogger("messages.log")
- ## var formatLog = newFileLogger("formatted.log", fmtStr=verboseFmtStr)
- ## var errorLog = newFileLogger("errors.log", levelThreshold=lvlError)
- ## ```
- let file = open(filename, mode, bufSize = bufSize)
- newFileLogger(file, levelThreshold, fmtStr, flushThreshold)
- # ------
- proc countLogLines(logger: RollingFileLogger): int =
- let fp = open(logger.baseName, fmRead)
- for line in fp.lines():
- result.inc()
- fp.close()
- proc countFiles(filename: string): int =
- # Example: file.log.1
- result = 0
- var (dir, name, ext) = splitFile(filename)
- if dir == "":
- dir = "."
- for kind, path in walkDir(dir):
- if kind == pcFile:
- let llfn = name & ext & ExtSep
- if path.extractFilename.startsWith(llfn):
- let numS = path.extractFilename[llfn.len .. ^1]
- try:
- let num = parseInt(numS)
- if num > result:
- result = num
- except ValueError: discard
- proc newRollingFileLogger*(filename = defaultFilename(),
- mode: FileMode = fmReadWrite,
- levelThreshold = lvlAll,
- fmtStr = defaultFmtStr,
- maxLines: Positive = 1000,
- bufSize: int = -1,
- flushThreshold = defaultFlushThreshold): RollingFileLogger =
- ## Creates a new `RollingFileLogger<#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<#newConsoleLogger>`_
- ## * `newFileLogger proc<#newFileLogger,File>`_ that uses a file handle
- ## * `newFileLogger proc<#newFileLogger,FileMode,int>`_
- ## that accepts a filename
- ##
- ## **Examples:**
- ##
- ## ```Nim
- ## 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)
- ## ```
- new(result)
- result.levelThreshold = levelThreshold
- result.fmtStr = fmtStr
- result.maxLines = maxLines
- result.bufSize = bufSize
- result.file = open(filename, mode, bufSize = result.bufSize)
- result.curLine = 0
- result.baseName = filename
- result.baseMode = mode
- result.flushThreshold = flushThreshold
- result.logFiles = countFiles(filename)
- if mode == fmAppend:
- # We need to get a line count because we will be appending to the file.
- result.curLine = countLogLines(result)
- proc rotate(logger: RollingFileLogger) =
- let (dir, name, ext) = splitFile(logger.baseName)
- for i in countdown(logger.logFiles, 0):
- let srcSuff = if i != 0: ExtSep & $i else: ""
- moveFile(dir / (name & ext & srcSuff),
- dir / (name & ext & ExtSep & $(i+1)))
- method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) =
- ## Logs a message at the specified level using the given
- ## `RollingFileLogger<#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<#setLogFilter,Level>`_.
- ##
- ## **Notes:**
- ## * Only error and fatal messages will cause the output buffer
- ## to be flushed immediately by default. Set ``flushThreshold`` when creating
- ## the logger to change this.
- ## * This method is not available for the JavaScript backend.
- ##
- ## See also:
- ## * `log method<#log.e,ConsoleLogger,Level,varargs[string,]>`_
- ## for the ConsoleLogger
- ## * `log method<#log.e,FileLogger,Level,varargs[string,]>`_
- ## for the FileLogger
- ## * `log template<#log.t,Level,varargs[string,]>`_
- ##
- ## **Examples:**
- ##
- ## ```Nim
- ## var rollingLog = newRollingFileLogger("messages.log")
- ## rollingLog.log(lvlInfo, "this is a message")
- ## rollingLog.log(lvlError, "error code is: ", 404)
- ## ```
- if level >= logging.level and level >= logger.levelThreshold:
- if logger.curLine >= logger.maxLines:
- logger.file.close()
- rotate(logger)
- logger.logFiles.inc
- logger.curLine = 0
- logger.file = open(logger.baseName, logger.baseMode,
- bufSize = logger.bufSize)
- writeLine(logger.file, substituteLog(logger.fmtStr, level, args))
- if level >= logger.flushThreshold: flushFile(logger.file)
- logger.curLine.inc
- # --------
- proc logLoop(level: Level, args: varargs[string, `$`]) =
- for logger in items(handlers):
- if level >= logger.levelThreshold:
- log(logger, level, args)
- 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<#setLogFilter,Level>`_.
- ##
- ## **Examples:**
- ##
- ## ```Nim
- ## var logger = newConsoleLogger()
- ## addHandler(logger)
- ##
- ## log(lvlInfo, "This is an example.")
- ## ```
- ##
- ## See also:
- ## * `debug template<#debug.t,varargs[string,]>`_
- ## * `info template<#info.t,varargs[string,]>`_
- ## * `notice template<#notice.t,varargs[string,]>`_
- ## * `warn template<#warn.t,varargs[string,]>`_
- ## * `error template<#error.t,varargs[string,]>`_
- ## * `fatal template<#fatal.t,varargs[string,]>`_
- bind logLoop
- bind `%`
- bind logging.level
- if level >= logging.level:
- logLoop(level, args)
- 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:**
- ##
- ## ```Nim
- ## var logger = newConsoleLogger()
- ## addHandler(logger)
- ##
- ## debug("myProc called with arguments: foo, 5")
- ## ```
- ##
- ## See also:
- ## * `log template<#log.t,Level,varargs[string,]>`_
- ## * `info template<#info.t,varargs[string,]>`_
- ## * `notice template<#notice.t,varargs[string,]>`_
- log(lvlDebug, args)
- 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:**
- ##
- ## ```Nim
- ## var logger = newConsoleLogger()
- ## addHandler(logger)
- ##
- ## info("Application started successfully.")
- ## ```
- ##
- ## See also:
- ## * `log template<#log.t,Level,varargs[string,]>`_
- ## * `debug template<#debug.t,varargs[string,]>`_
- ## * `notice template<#notice.t,varargs[string,]>`_
- log(lvlInfo, args)
- 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:**
- ##
- ## ```Nim
- ## var logger = newConsoleLogger()
- ## addHandler(logger)
- ##
- ## notice("An important operation has completed.")
- ## ```
- ##
- ## See also:
- ## * `log template<#log.t,Level,varargs[string,]>`_
- ## * `debug template<#debug.t,varargs[string,]>`_
- ## * `info template<#info.t,varargs[string,]>`_
- log(lvlNotice, args)
- 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:**
- ##
- ## ```Nim
- ## var logger = newConsoleLogger()
- ## addHandler(logger)
- ##
- ## warn("The previous operation took too long to process.")
- ## ```
- ##
- ## See also:
- ## * `log template<#log.t,Level,varargs[string,]>`_
- ## * `error template<#error.t,varargs[string,]>`_
- ## * `fatal template<#fatal.t,varargs[string,]>`_
- log(lvlWarn, args)
- 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:**
- ##
- ## ```Nim
- ## var logger = newConsoleLogger()
- ## addHandler(logger)
- ##
- ## error("An exception occurred while processing the form.")
- ## ```
- ##
- ## See also:
- ## * `log template<#log.t,Level,varargs[string,]>`_
- ## * `warn template<#warn.t,varargs[string,]>`_
- ## * `fatal template<#fatal.t,varargs[string,]>`_
- log(lvlError, args)
- 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:**
- ##
- ## ```Nim
- ## var logger = newConsoleLogger()
- ## addHandler(logger)
- ##
- ## fatal("Can't open database -- exiting.")
- ## ```
- ##
- ## See also:
- ## * `log template<#log.t,Level,varargs[string,]>`_
- ## * `warn template<#warn.t,varargs[string,]>`_
- ## * `error template<#error.t,varargs[string,]>`_
- log(lvlFatal, args)
- proc addHandler*(handler: Logger) =
- ## 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:
- ## * `removeHandler proc`_
- ## * `getHandlers proc<#getHandlers>`_
- runnableExamples:
- var logger = newConsoleLogger()
- addHandler(logger)
- doAssert logger in getHandlers()
- handlers.add(handler)
- proc removeHandler*(handler: Logger) =
- ## Removes a logger from the list of registered handlers.
- ##
- ## Note that for n times a logger is registered, n calls to this proc
- ## are required to remove that logger.
- for i, hnd in handlers:
- if hnd == handler:
- handlers.delete(i)
- return
- proc getHandlers*(): seq[Logger] =
- ## Returns a list of all the registered handlers.
- ##
- ## See also:
- ## * `addHandler proc<#addHandler,Logger>`_
- return handlers
- proc setLogFilter*(lvl: Level) =
- ## 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:
- ## * `getLogFilter proc<#getLogFilter>`_
- runnableExamples:
- setLogFilter(lvlError)
- doAssert getLogFilter() == lvlError
- level = lvl
- proc getLogFilter*(): Level =
- ## Gets the global log filter.
- ##
- ## See also:
- ## * `setLogFilter proc<#setLogFilter,Level>`_
- return level
- # --------------
- when not defined(testing) and isMainModule:
- var L = newConsoleLogger()
- when not defined(js):
- var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
- var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
- addHandler(fL)
- addHandler(rL)
- addHandler(L)
- for i in 0 .. 25:
- info("hello", i)
- var nilString: string
- info "hello ", nilString
|