summary refs log tree commit diff stats
path: root/lib/pure/logging.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/logging.nim')
-rw-r--r--lib/pure/logging.nim114
1 files changed, 66 insertions, 48 deletions
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index 9247f69c5..a2ea53472 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -1,16 +1,16 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
+#        (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 fullfill your needs,
+## as possible to avoid bloat, if this library does not fulfill your needs,
 ## write your own.
-## 
+##
 ## Format strings support the following variables which must be prefixed with
 ## the dollar operator (``$``):
 ##
@@ -21,23 +21,26 @@
 ## $time         Current time
 ## $app          ``os.getAppFilename()``
 ## ============  =======================
-## 
+##
 ##
 ## The following example demonstrates logging to three different handlers
 ## simultaneously:
 ##
 ## .. code-block:: nim
-##     
+##
 ##    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)
+##    addHandler(L)
+##    addHandler(fL)
+##    addHandler(rL)
 ##    info("920410:52 accepted")
 ##    warn("4 8 15 16 23 4-- Error")
 ##    error("922044:16 SYSTEM FAILURE")
 ##    fatal("SYSTEM FAILURE SYSTEM FAILURE")
+##
+## **Warning:** The global list of handlers is a thread var, this means that
+## the handlers must be re-added in each thread.
 
 import strutils, os, times
 
@@ -61,20 +64,20 @@ const
 
 type
   Logger* = ref object of RootObj ## abstract logger; the base type of all loggers
-    levelThreshold*: Level    ## only messages of level >= levelThreshold 
+    levelThreshold*: Level    ## only messages of level >= levelThreshold
                               ## should be processed
     fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
-    
+
   ConsoleLogger* = ref object of Logger ## logger that writes the messages to the
                                         ## console
-  
+
   FileLogger* = ref object of Logger ## logger that writes the messages to a file
     f: File
-  
-  RollingFileLogger* = ref object of FileLogger ## logger that writes the 
+
+  RollingFileLogger* = ref object of FileLogger ## logger that writes the
                                                 ## messages to a file and
                                                 ## performs log rotation
-    maxLines: int # maximum number of lines    
+    maxLines: int # maximum number of lines
     curLine : int
     baseName: string # initial filename
     baseMode: FileMode # initial file mode
@@ -83,22 +86,22 @@ type
 {.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger,
     PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
 
-proc substituteLog(frmt: string): string = 
+proc substituteLog(frmt: string): string =
   ## converts $date to the current date
   ## converts $time to the current time
   ## converts $app to getAppFilename()
-  ## converts 
+  ## converts
   result = newStringOfCap(frmt.len + 20)
   var i = 0
-  while i < frmt.len: 
-    if frmt[i] != '$': 
+  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: 
+      while frmt[i] in IdentChars:
         v.add(toLower(frmt[i]))
         inc(i)
       case v
@@ -111,12 +114,12 @@ proc substituteLog(frmt: string): string =
 
 method log*(logger: Logger, level: Level,
             frmt: string, args: varargs[string, `$`]) {.
-            raises: [Exception], 
+            raises: [Exception],
             tags: [TimeEffect, WriteIOEffect, ReadIOEffect].} =
   ## Override this method in custom loggers. Default implementation does
   ## nothing.
   discard
-  
+
 method log*(logger: ConsoleLogger, level: Level,
             frmt: string, args: varargs[string, `$`]) =
   ## Logs to the console using ``logger`` only.
@@ -124,14 +127,14 @@ method log*(logger: ConsoleLogger, level: Level,
     writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr),
             frmt % args)
 
-method log*(logger: FileLogger, level: Level, 
+method log*(logger: FileLogger, level: Level,
             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 = 
+proc defaultFilename*(): string =
   ## Returns the default filename for a logger.
   var (path, name, ext) = splitFile(getAppFilename())
   result = changeFileExt(path / name, "log")
@@ -142,10 +145,10 @@ proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): Console
   result.fmtStr = fmtStr
   result.levelThreshold = levelThreshold
 
-proc newFileLogger*(filename = defaultFilename(), 
+proc newFileLogger*(filename = defaultFilename(),
                     mode: FileMode = fmAppend,
                     levelThreshold = lvlAll,
-                    fmtStr = defaultFmtStr): FileLogger = 
+                    fmtStr = defaultFmtStr): FileLogger =
   ## Creates a new file logger. This logger logs to a file.
   new(result)
   result.levelThreshold = levelThreshold
@@ -167,14 +170,14 @@ proc countFiles(filename: string): int =
     if kind == pcFile:
       let llfn = name & ext & ExtSep
       if path.extractFilename.startsWith(llfn):
-        let numS = path.extractFilename[llfn.len .. -1]
+        let numS = path.extractFilename[llfn.len .. ^1]
         try:
           let num = parseInt(numS)
           if num > result:
             result = num
         except ValueError: discard
 
-proc newRollingFileLogger*(filename = defaultFilename(), 
+proc newRollingFileLogger*(filename = defaultFilename(),
                            mode: FileMode = fmReadWrite,
                            levelThreshold = lvlAll,
                            fmtStr = defaultFmtStr,
@@ -183,15 +186,15 @@ proc newRollingFileLogger*(filename = defaultFilename(),
   ## a new log file will be started and the old will be renamed.
   new(result)
   result.levelThreshold = levelThreshold
-  result.fmtStr = defaultFmtStr
+  result.fmtStr = fmtStr
   result.maxLines = maxLines
   result.f = open(filename, mode)
   result.curLine = 0
   result.baseName = filename
   result.baseMode = mode
-  
+
   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)
@@ -203,7 +206,7 @@ proc rotate(logger: RollingFileLogger) =
     moveFile(dir / (name & ext & srcSuff),
              dir / (name & ext & ExtSep & $(i+1)))
 
-method log*(logger: RollingFileLogger, level: Level, 
+method log*(logger: RollingFileLogger, level: Level,
             frmt: string, args: varargs[string, `$`]) =
   ## Logs to a file using rolling ``logger`` only.
   if level >= logger.levelThreshold:
@@ -213,18 +216,17 @@ method log*(logger: RollingFileLogger, level: Level,
       logger.logFiles.inc
       logger.curLine = 0
       logger.f = open(logger.baseName, logger.baseMode)
-    
-    writeln(logger.f, LevelNames[level], " ", frmt % args)
+
+    writeln(logger.f, LevelNames[level], " ",substituteLog(logger.fmtStr), frmt % args)
     logger.curLine.inc
 
 # --------
 
-var
-  level* = lvlAll  ## global log filter
-  handlers*: seq[Logger] = @[] ## handlers with their own log levels
+var level {.threadvar.}: Level   ## global log filter
+var handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels
 
 proc logLoop(level: Level, frmt: string, args: varargs[string, `$`]) =
-  for logger in items(handlers): 
+  for logger in items(handlers):
     if level >= logger.levelThreshold:
       log(logger, level, frmt, args)
 
@@ -233,7 +235,7 @@ template log*(level: Level, frmt: string, args: varargs[string, `$`]) =
   bind logLoop
   bind `%`
   bind logging.level
-  
+
   if level >= logging.level:
     logLoop(level, frmt, args)
 
@@ -241,33 +243,49 @@ template debug*(frmt: string, args: varargs[string, `$`]) =
   ## Logs a debug message to all registered handlers.
   log(lvlDebug, frmt, args)
 
-template info*(frmt: string, args: varargs[string, `$`]) = 
+template info*(frmt: string, args: varargs[string, `$`]) =
   ## Logs an info message to all registered handlers.
   log(lvlInfo, frmt, args)
 
-template warn*(frmt: string, args: varargs[string, `$`]) = 
+template warn*(frmt: string, args: varargs[string, `$`]) =
   ## Logs a warning message to all registered handlers.
   log(lvlWarn, frmt, args)
 
-template error*(frmt: string, args: varargs[string, `$`]) = 
+template error*(frmt: string, args: varargs[string, `$`]) =
   ## Logs an error message to all registered handlers.
   log(lvlError, frmt, args)
-  
-template fatal*(frmt: string, args: varargs[string, `$`]) =  
+
+template fatal*(frmt: string, args: varargs[string, `$`]) =
   ## Logs a fatal error message to all registered handlers.
   log(lvlFatal, frmt, args)
 
+proc addHandler*(handler: Logger) =
+  ## Adds ``handler`` to the list of handlers.
+  if handlers.isNil: handlers = @[]
+  handlers.add(handler)
+
+proc getHandlers*(): seq[Logger] =
+  ## Returns a list of all the registered handlers.
+  return handlers
+
+proc setLogFilter*(lvl: Level) =
+  ## Sets the global log filter.
+  level = lvl
+
+proc getLogFilter*(): Level =
+  ## Gets the global log filter.
+  return level
 
 # --------------
 
-when isMainModule:
+when not defined(testing) and isMainModule:
   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)
+  addHandler(L)
+  addHandler(fL)
+  addHandler(rL)
   for i in 0 .. 25:
     info("hello" & $i, [])
-  
+