diff options
-rw-r--r-- | devel/logging.nim | 149 |
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", []) + |