<commit_before>#
#
#            Nimrod's Runtime Library
#        (c) Copyright 2014 <NAME>, <NAME>
#
#    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 fullfill your needs,
## write your own.
## 
## Format strings support the following variables which must be prefixed with
## the dollar operator (``$``):
##
## ============  =======================
##   Operator     Output
## ============  =======================
## $date         Current date
## $time         Current time
## $app          ``os.getAppFilename()``
## ============  =======================
## 
##
## The following example demonstrates logging to three different handlers
## simultaneously:
##
## .. code-block:: nimrod
##     
##    var L = newConsoleLogger()
##    var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
##    var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
##    handlers.add(L)
##    handlers.add(fL)
##    handlers.add(rL)
##    info("920410:52 accepted")
##    warn("4 8 15 16 23 4-- Error")
##    error("922044:16 SYSTEM FAILURE")
##    fatal("SYSTEM FAILURE SYSTEM FAILURE")

import strutils, os, times

type
  TLevel* = enum  ## logging level
    lvlAll,       ## all levels active
    lvlDebug,     ## debug level (and any above) active
    lvlInfo,      ## info level (and any above) active
    lvlWarn,      ## warn level (and any above) active
    lvlError,     ## error level (and any above) active
    lvlFatal,     ## fatal level (and any above) active
    lvlNone       ## no levels active

const
  LevelNames*: array [TLevel, string] = [
    "DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
  ]

  defaultFmtStr* = "" ## default string between log level and message per logger
  verboseFmtStr* = "$date $time "

type
  PLogger* = ref object of PObject ## abstract logger; the base type of all loggers
    levelThreshold*: TLevel    ## only messages of level >= levelThreshold 
                               ## should be processed
    fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
    
  PConsoleLogger* = ref object of PLogger ## logger that writes the messages to the
                                      ## console
  
  PFileLogger* = ref object of PLogger ## logger that writes the messages to a file
    f: TFile
  
  PRollingFileLogger* = ref object of PFileLogger ## logger that writes the 
                                                  ## messages to a file and
                                                  ## performs log rotation
    maxLines: int # maximum number of lines    
    curLine : int
    baseName: string # initial filename
    baseMode: TFileMode # initial file mode
    logFiles: int # how many log files already created, e.g. basename.1, basename.2...

proc substituteLog(frmt: string): string = 
  ## converts $date to the current date
  ## converts $time to the current time
  ## converts $app to getAppFilename()
  ## converts 
  result = newStringOfCap(frmt.len + 20)
  var i = 0
  while i < frmt.len: 
    if frmt[i] != '$': 
      result.add(frmt[i])
      inc(i)
    else:
      inc(i)
      var v = ""
      var app = getAppFilename()
      while frmt[i] in IdentChars: 
        v.add(toLower(frmt[i]))
        inc(i)
      case v
      of "date": result.add(getDateStr())
      of "time": result.add(getClockStr())
      of "app":  result.add(app)
      of "appdir": result.add(app.splitFile.dir)
      of "appname": result.add(app.splitFile.name)

method log*(logger: PLogger, level: TLevel,
            frmt: string, args: varargs[string, `$`]) =
  ## Override this method in custom loggers. Default implementation does
  ## nothing.
  nil
  
method log*(logger: PConsoleLogger, level: TLevel,
            frmt: string, args: varargs[string, `$`]) =
  ## Logs to the console using ``logger`` only.
  if level >= logger.levelThreshold:
    writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr),
            frmt % args)

method log*(logger: PFileLogger, level: TLevel, 
            frmt: string, args: varargs[string, `$`]) =
  ## Logs to a file using ``logger`` only.
  if level >= logger.levelThreshold:
    writeln(logger.f, LevelNames[level], " ",
            substituteLog(logger.fmtStr), frmt % args)

proc defaultFilename*(): string = 
  ## Returns the default filename for a logger.
  var (path, name, ext) = splitFile(getAppFilename())
  result = changeFileExt(path / name, "log")

proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): PConsoleLogger =
  ## Creates a new console logger. This logger logs to the console.<commit_msg>Fixes docgen for logging module.<commit_after>#
#
#            Nimrod's Runtime Library
#        (c) Copyright 2014 <NAME>, <NAME>
#
#    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 fullfill your needs,
## write your own.
## 
## Format strings support the following variables which must be prefixed with
## the dollar operator (``$``):
##
## ============  =======================
##   Operator     Output
## ============  =======================
## $date         Current date
## $time         Current time
## $app          ``os.getAppFilename()``
## ============  =======================
## 
##
## The following example demonstrates logging to three different handlers
## simultaneously:
##
## .. code-block:: nimrod
##     
##    var L = newConsoleLogger()
##    var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
##    var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
##    handlers.add(L)
##    handlers.add(fL)
##    handlers.add(rL)
##    info("920410:52 accepted")
##    warn("4 8 15 16 23 4-- Error")
##    error("922044:16 SYSTEM FAILURE")
##    fatal("SYSTEM FAILURE SYSTEM FAILURE")

import strutils, os, times

type
  TLevel* = enum  ## logging level
    lvlAll,       ## all levels active
    lvlDebug,     ## debug level (and any above) active
    lvlInfo,      ## info level (and any above) active
    lvlWarn,      ## warn level (and any above) active
    lvlError,     ## error level (and any above) active
    lvlFatal,     ## fatal level (and any above) active
    lvlNone       ## no levels active

const
  LevelNames*: array [TLevel, string] = [
    "DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
  ]

  defaultFmtStr* = "" ## default string between log level and message per logger
  verboseFmtStr* = "$date $time "

type
  PLogger* = ref object of PObject ## abstract logger; the base type of all loggers
    levelThreshold*: TLevel    ## only messages of level >= levelThreshold 
                               ## should be processed
    fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
    
  PConsoleLogger* = ref object of PLogger ## logger that writes the messages to the
                                      ## console
  
  PFileLogger* = ref object of PLogger ## logger that writes the messages to a file
    f: TFile
  
  PRollingFileLogger* = ref object of PFileLogger ## logger that writes the 
                                                  ## messages to a file and
                                                  ## performs log rotation
    maxLines: int # maximum number of lines    
    curLine : int
    baseName: string # initial filename
    baseMode: TFileMode # initial file mode
    logFiles: int # how many log files already created, e.g. basename.1, basename.2...

proc substituteLog(frmt: string): string = 
  ## converts $date to the current date
  ## converts $time to the current time
  ## converts $app to getAppFilename()
  ## converts 
  result = newStringOfCap(frmt.len + 20)
  var i = 0
  while i < frmt.len: 
    if frmt[i] != '$': 
      result.add(frmt[i])
      inc(i)
    else:
      inc(i)
      var v = ""
      var app = getAppFilename()
      while frmt[i] in IdentChars: 
        v.add(toLower(frmt[i]))
        inc(i)
      case v
      of "date": result.add(getDateStr())
      of "time": result.add(getClockStr())
      of "app":  result.add(app)
      of "appdir": result.add(app.splitFile.dir)
      of "appname": result.add(app.splitFile.name)

method log*(logger: PLogger, level: TLevel,
            frmt: string, args: varargs[string, `$`]) {.raises: [EBase], tags: [FTime, FWriteIO, FReadIO].} =
  ## Override this method in custom loggers. Default implementation does
  ## nothing.
  discard
  
method log*(logger: PConsoleLogger, level: TLevel,
            frmt: string, args: varargs[string, `$`]) =
  ## Logs to the console using ``logger`` only.
  if level >= logger.levelThreshold:
    writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr),
            frmt % args)

method log*(logger: PFileLogger, level: TLevel, 
            frmt: string, args: varargs[string, `$`]) =
  ## Logs to a file using ``logger`` only.
  if level >= logger.levelThreshold:
    writeln(logger.f, LevelNames[level], " ",
            substituteLog(logger.fmtStr), frmt % args)

proc defaultFilename*(): string = 
  ## Returns the default filename for a logger.
  var (path, name, ext) = splitFile(getAppFilename())
  result = changeFileExt(path / name, "log")

proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): PConsoleLogger =
  ## Creates a new console logger. This logger logs to the console.
{'fraction_non_alphanumeric': 0.1355193524955899, 'fraction_numerical': 0.007056137802220608, 'mean_word_length': 2.76484375, 'pattern_counts': {'":': 10, '<': 9, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7516985', 'n_tokens_mistral': 2899, 'n_tokens_neox': 2782, 'n_words': 953}