summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--devel/logging.nim149
1 files changed, 107 insertions, 42 deletions
diff --git a/devel/logging.nim b/devel/logging.nim
index 7ae4d7eee..57b34ce5e 100644
--- a/devel/logging.nim
+++ b/devel/logging.nim
@@ -18,6 +18,8 @@
 ##
 ## 
 
+import strutils, os, times
+
 type
   TLevel* = enum  ## logging level
     lvlAll,       ## all levels active
@@ -25,46 +27,39 @@ type
     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
+    lvlFatal,     ## fatal level (and any above) active
+    lvlNone
 
 const
   LevelNames*: array [TLevel, string] = [
-    "DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
+    "DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
   ]
 
+  defaultFmtStr = "" ## default string between log level and message per logger
+  verboseFmtStr = "$date $time "
+
 type
   TLogger* = object of TObject ## 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.
+    
   TConsoleLogger* = object of TLogger ## logger that writes the messages to the
                                       ## console
   
   TFileLogger* = object of TLogger ## logger that writes the messages to a file
     f: TFile
-    
+  
+  # TODO: implement rolling log, will produce filename.1, filename.2 etc.
   TRollingFileLogger* = object of TFileLogger ## logger that writes the 
                                               ## message to a file
-    maxlines: int # maximum number of lines
-    lines: seq[string]
-
-method log*(L: ref TLogger, level: TLevel,
-            frmt: string, args: openArray[string]) =
-  ## override this method in custom loggers. Default implementation does
-  ## nothing.
-  nil
-  
-method log*(L: ref TConsoleLogger, level: TLevel,
-            frmt: string, args: openArray[string]) = 
-  Writeln(stdout, LevelNames[level], " ", frmt % args)
-
-method log*(L: ref TFileLogger, level: TLevel, 
-            frmt: string, args: openArray[string]) = 
-  Writeln(L.f, LevelNames[level], " ", frmt % args)
+    maxLines: int # maximum number of lines    
+    curLine : int
+    baseName: string # initial filename
+    logFiles: int # how many log files already created, e.g. basename.1, basename.2...
+    
+    
 
-proc defaultFilename*(): string = 
-  ## returns the default filename for a logger
-  var (path, name, ext) = splitFile(getAppFilename())
-  result = changeFileExt(path / name & "_" & getDateStr(), "log")
 
 proc substituteLog*(frmt: string): string = 
   ## converts $date to the current date
@@ -90,56 +85,126 @@ proc substituteLog*(frmt: string): string =
       of "app":  result.add(app)
       of "appdir": result.add(app.splitFile.dir)
       of "appname": result.add(app.splitFile.name)
+
+
+
+method log*(L: ref TLogger, level: TLevel,
+            frmt: string, args: openArray[string]) =
+  ## override this method in custom loggers. Default implementation does
+  ## nothing.
+  nil
+  
+method log*(L: ref TConsoleLogger, level: TLevel,
+            frmt: string, args: openArray[string]) = 
+    Writeln(stdout, LevelNames[level], " ", substituteLog(L.fmtStr), frmt % args)
+
+method log*(L: ref TFileLogger, level: TLevel, 
+            frmt: string, args: openArray[string]) = 
+    Writeln(L.f, LevelNames[level], " ", substituteLog(L.fmtStr), frmt % args)
+
+proc defaultFilename*(): string = 
+  ## returns the default filename for a logger
+  var (path, name, ext) = splitFile(getAppFilename())
+  result = changeFileExt(path / name & "_" & getDateStr(), "log")
+
       
 
+
+proc newConsoleLogger*(levelThreshold = lvlAll) : ref TConsoleLogger =
+  new result
+  result.fmtStr = defaultFmtStr
+  result.levelThreshold = levelThreshold
+
 proc newFileLogger*(filename = defaultFilename(), 
                     mode: TFileMode = fmAppend,
-                    levelThreshold = lvlNone): ref TFileLogger = 
+                    levelThreshold = lvlAll): ref TFileLogger = 
   new(result)
   result.levelThreshold = levelThreshold
   result.f = open(filename, mode)
+  result.fmtStr = defaultFmtStr
+
+# ------
+
+proc readLogLines(logger : ref TRollingFileLogger) = nil
+  #f.readLine # TODO read all lines, update curLine
+
 
 proc newRollingFileLogger*(filename = defaultFilename(), 
-                           mode: TFileMode = fmAppend,
-                           levelThreshold = lvlNone,
-                           maxLines = 1000): ref TFileLogger = 
+                           mode: TFileMode = fmReadWrite,
+                           levelThreshold = lvlAll,
+                           maxLines = 1000): ref TRollingFileLogger = 
   new(result)
   result.levelThreshold = levelThreshold
+  result.fmtStr = defaultFmtStr
   result.maxLines = maxLines
   result.f = open(filename, mode)
+  result.curLine = 0
+  
+  # TODO count all number files
+  # count lines in existing filename file
+  # if >= maxLines then rename to next numbered file and create new file
+  
+  #if mode in {fmReadWrite, fmReadWriteExisting}:
+  #  readLogLines(result)
+
+
+
+method log*(L: ref TRollingFileLogger, level: TLevel, 
+            frmt: string, args: openArray[string]) = 
+  # TODO 
+  # if more than maxlines, then set cursor to zero
+  
+  Writeln(L.f, LevelNames[level], " ", frmt % args)
+
+# --------
 
 var
-  level* = lvlNone
-  handlers*: seq[ref TLogger] = @[]
+  level* = lvlAll  ## global log filter
+  handlers*: seq[ref TLogger] = @[] ## handlers with their own log levels
 
-proc logLoop(level: TLevel, msg: string) =
+proc logLoop(level: TLevel, frmt: string, args: openarray[string]) =
   for logger in items(handlers): 
     if level >= logger.levelThreshold:
-      log(logger, level, msg)
+      log(logger, level, frmt, args)
 
-template log*(level: TLevel, msg: string) =
+template log*(level: TLevel, frmt: string, args: openarray[string]) =
   ## logs a message of the given level
   bind logLoop
+  bind `%`
+  bind logging.Level
+  
   if level >= logging.Level:
     logLoop(level, frmt, args)
 
-template debug*(msg: string) =
+template debug*(frmt: string, args: openarray[string]) =
   ## logs a debug message
-  log(lvlDebug, msg)
+  log(lvlDebug, frmt, args)
 
-template info*(msg: string) = 
+template info*(frmt: string, args: openarray[string]) = 
   ## logs an info message
-  log(lvlInfo, msg)
+  log(lvlInfo, frmt, args)
 
-template warn*(msg: string) = 
+template warn*(frmt: string, args: openarray[string]) = 
   ## logs a warning message
-  log(lvlWarn, msg)
+  log(lvlWarn, frmt, args)
 
-template error*(msg: string) = 
+template error*(frmt: string, args: openarray[string]) = 
   ## logs an error message
-  log(lvlError, msg)
+  log(lvlError, frmt, args)
   
-template fatal*(msg: string) =  
+template fatal*(frmt: string, args: openarray[string]) =  
   ## logs a fatal error message
-  log(lvlFatal, msg)
+  log(lvlFatal, frmt, args)
+
+
+# --------------
+
+when isMainModule:
+  var L = newConsoleLogger()
+  var fL = newFileLogger("test.log")
+  fL.fmtStr = verboseFmtStr
+  handlers.add(L)
+  handlers.add(fL)
+  info("hello", [])
+