155 lines
4.1 KiB
Nim
155 lines
4.1 KiB
Nim
# SPDX-License-Identifier: LSL-1.0
|
|
# Copyright (c) 2026 Markus Maiwald
|
|
# Stewardship: Self Sovereign Society Foundation
|
|
#
|
|
# This file is part of the Nexus Sovereign Core.
|
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
|
|
|
## logger.nim
|
|
## Logging system for NIP MVP
|
|
|
|
import std/[times, strformat, os, strutils]
|
|
|
|
type
|
|
LogLevel* = enum
|
|
Debug, Info, Warning, Error, Fatal
|
|
|
|
Logger* = ref object
|
|
logFile*: string
|
|
minLevel*: LogLevel
|
|
enabled*: bool
|
|
verbose*: bool
|
|
|
|
var globalLogger*: Logger = nil
|
|
|
|
proc newLogger*(logFile: string = "/var/log/nip.log",
|
|
minLevel: LogLevel = Info,
|
|
verbose: bool = false): Logger =
|
|
## Create a new logger
|
|
result = Logger(
|
|
logFile: logFile,
|
|
minLevel: minLevel,
|
|
enabled: true,
|
|
verbose: verbose
|
|
)
|
|
|
|
proc initGlobalLogger*(logFile: string = "/var/log/nip.log",
|
|
minLevel: LogLevel = Info,
|
|
verbose: bool = false) =
|
|
## Initialize the global logger
|
|
globalLogger = newLogger(logFile, minLevel, verbose)
|
|
|
|
proc levelToString(level: LogLevel): string =
|
|
case level
|
|
of Debug: "DEBUG"
|
|
of Info: "INFO"
|
|
of Warning: "WARN"
|
|
of Error: "ERROR"
|
|
of Fatal: "FATAL"
|
|
|
|
proc log*(logger: Logger, level: LogLevel, message: string) =
|
|
## Log a message
|
|
if not logger.enabled:
|
|
return
|
|
|
|
if level < logger.minLevel:
|
|
return
|
|
|
|
let timestamp = now().format("yyyy-MM-dd HH:mm:ss")
|
|
let levelStr = levelToString(level)
|
|
let logLine = fmt"[{timestamp}] [{levelStr}] {message}"
|
|
|
|
# Print to console if verbose or error/fatal
|
|
if logger.verbose or level >= Error:
|
|
echo logLine
|
|
|
|
# Write to log file
|
|
try:
|
|
let logDir = parentDir(logger.logFile)
|
|
if not dirExists(logDir):
|
|
try:
|
|
createDir(logDir)
|
|
except:
|
|
# Can't create log directory, skip file logging
|
|
return
|
|
|
|
let file = open(logger.logFile, fmAppend)
|
|
file.writeLine(logLine)
|
|
file.close()
|
|
except IOError, OSError:
|
|
# Can't write to log file, just skip it
|
|
discard
|
|
|
|
# Convenience functions for global logger
|
|
proc logDebug*(message: string) =
|
|
if globalLogger != nil:
|
|
globalLogger.log(Debug, message)
|
|
|
|
proc logInfo*(message: string) =
|
|
if globalLogger != nil:
|
|
globalLogger.log(Info, message)
|
|
|
|
proc logWarning*(message: string) =
|
|
if globalLogger != nil:
|
|
globalLogger.log(Warning, message)
|
|
|
|
proc logError*(message: string) =
|
|
if globalLogger != nil:
|
|
globalLogger.log(Error, message)
|
|
|
|
proc logFatal*(message: string) =
|
|
if globalLogger != nil:
|
|
globalLogger.log(Fatal, message)
|
|
|
|
# Operation logging helpers
|
|
proc logOperation*(operation: string, details: string = "") =
|
|
let msg = if details != "": fmt"{operation}: {details}" else: operation
|
|
logInfo(msg)
|
|
|
|
proc logSuccess*(operation: string, details: string = "") =
|
|
let msg = if details != "": fmt"✅ {operation}: {details}" else: fmt"✅ {operation}"
|
|
logInfo(msg)
|
|
|
|
proc logFailure*(operation: string, error: string) =
|
|
logError(fmt"❌ {operation} failed: {error}")
|
|
|
|
proc logException*(operation: string, e: ref Exception) =
|
|
logError(fmt"Exception in {operation}: {e.msg}")
|
|
if globalLogger != nil and globalLogger.verbose:
|
|
logError(e.getStackTrace())
|
|
|
|
# Log rotation
|
|
proc rotateLog*(logger: Logger, maxSize: int64 = 10_000_000) =
|
|
## Rotate log file if it exceeds maxSize (default 10MB)
|
|
if not fileExists(logger.logFile):
|
|
return
|
|
|
|
try:
|
|
let size = getFileSize(logger.logFile)
|
|
if size > maxSize:
|
|
# Rotate: nip.log -> nip.log.1, nip.log.1 -> nip.log.2, etc.
|
|
for i in countdown(2, 1):
|
|
let oldFile = logger.logFile & "." & $i
|
|
let newFile = logger.logFile & "." & $(i + 1)
|
|
if fileExists(oldFile):
|
|
moveFile(oldFile, newFile)
|
|
|
|
# Move current log to .1
|
|
moveFile(logger.logFile, logger.logFile & ".1")
|
|
|
|
logInfo("Log file rotated")
|
|
except:
|
|
discard
|
|
|
|
proc getLogPath*(): string =
|
|
## Get the current log file path
|
|
if globalLogger != nil:
|
|
return globalLogger.logFile
|
|
else:
|
|
return "/var/log/nip.log"
|
|
|
|
proc setVerbose*(verbose: bool) =
|
|
## Set verbose mode for global logger
|
|
if globalLogger != nil:
|
|
globalLogger.verbose = verbose
|