Add --compact option for simpler output.

This commit is contained in:
Jonathan Bernard 2025-01-19 18:40:38 -06:00
parent 34add1a729
commit 6c978f32cc
2 changed files with 45 additions and 10 deletions

View File

@ -1,6 +1,6 @@
# Package # Package
version = "0.1.2" version = "0.2.0"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Small utility to pretty-print strucutured logs." description = "Small utility to pretty-print strucutured logs."
license = "MIT" license = "MIT"
@ -11,4 +11,4 @@ bin = @["slfmt"]
# Dependencies # Dependencies
requires @["nim >= 2.2.0", "docopt"] requires @["nim >= 2.2.0", "docopt"]
requires "timeutils" requires @["timeutils", "zero_functional"]

View File

@ -1,10 +1,10 @@
import std/[json, options, sequtils, streams, strutils, terminal, times] import std/[json, options, sequtils, streams, strutils, terminal, times]
import timeutils import docopt, timeutils, zero_functional
import docopt
from std/logging import Level from std/logging import Level
from std/sequtils import toSeq
const VERSION = "0.1.2" const VERSION = "0.2.0"
const USAGE = """Usage: const USAGE = """Usage:
slfmt [options] slfmt [options]
@ -12,6 +12,7 @@ const USAGE = """Usage:
Options: Options:
-h, --help Print this usage and help information -h, --help Print this usage and help information
-c, --compact Compact output
-l, --log-level <lvl> Only show log events at or above this level -l, --log-level <lvl> Only show log events at or above this level
-n, --namespace <ns> Only show log events from this namespace -n, --namespace <ns> Only show log events from this namespace
""" """
@ -44,7 +45,7 @@ func decorate(
result &= s & ansiResetCode result &= s & ansiResetCode
proc formatField(name: string, value: JsonNode): string = proc fullFormatField(name: string, value: JsonNode): string =
result = decorate(name, fgCyan) & ":" & " ".repeat(max(1, 10 - name.len)) result = decorate(name, fgCyan) & ":" & " ".repeat(max(1, 10 - name.len))
var strVal: string = "" var strVal: string = ""
@ -66,20 +67,51 @@ proc formatField(name: string, value: JsonNode): string =
result &= "\n" & valLines.mapIt(" " & it).join("\n") & "\n" result &= "\n" & valLines.mapIt(" " & it).join("\n") & "\n"
else: result &= strVal & "\n" else: result &= strVal & "\n"
proc prettyPrintFormat(logJson: JsonNode): string = proc fullFormat(logJson: JsonNode): string =
result = '-'.repeat(terminalWidth()) & "\n" result = '-'.repeat(terminalWidth()) & "\n"
# Print the known fields in order first # Print the known fields in order first
for f in fieldDisplayOrder: for f in fieldDisplayOrder:
if logJson.hasKey(f): if logJson.hasKey(f):
result &= formatField(f, logJson[f]) result &= fullFormatField(f, logJson[f])
logJson.delete(f) logJson.delete(f)
# Print the rest of the fields # Print the rest of the fields
for (key, val) in pairs(logJson): result &= formatField(key, val) for (key, val) in pairs(logJson): result &= fullFormatField(key, val)
result &= "\n" result &= "\n"
proc compactFormat(logJson: JsonNode): string =
let ts = parseIso8601(logJson["ts"].getStr).local.formatIso8601
var level = logJson["level"].getStr
if level == "ERROR": level = decorate(alignLeft(level, 7), fgRed)
elif level == "WARN": level = decorate(alignLeft(level, 7), fgYellow)
else: level = alignLeft(level, 7)
result = "$1 $2 $3: $4" % [
level,
decorate(alignLeft(ts[0..21], 23), fgBlue, {styleBright}),
decorate(logJson["scope"].getStr, fgGreen),
decorate(logJson["msg"].getStr, fgYellow)]
let restNodes = (toSeq(pairs(logJson))) -->
filter(not ["level", "scope", "ts", "msg"].contains(it[0]))
let restMsg = join(restNodes -->
map("$1: $2" % [decorate(it[0], fgCyan), it[1].getStr]), " ")
if restMsg.len + result.len + 2 < terminalWidth():
result &= " " & restMsg
else:
var line = " "
for (key, val) in restNodes:
let field = "$1: $2" % [decorate(key, fgCyan), val.getStr]
if line.len + field.len + 2 > terminalWidth():
result &= "\n " & line
line = " "
line &= field & " "
result &= "\n " & line
proc parseLogLine(logLine: string): JsonNode = proc parseLogLine(logLine: string): JsonNode =
result = parseJson(logLine) result = parseJson(logLine)
@ -95,6 +127,8 @@ when isMainModule:
if args["--namespace"]: some($args["--namespace"]) if args["--namespace"]: some($args["--namespace"])
else: none[string]() else: none[string]()
let compact = args["--compact"]
var line: string = "" var line: string = ""
let sin = newFileStream(stdin) let sin = newFileStream(stdin)
while(sin.readLine(line)): while(sin.readLine(line)):
@ -108,7 +142,8 @@ when isMainModule:
if namespace.isSome and logJson.hasKey("scope"): if namespace.isSome and logJson.hasKey("scope"):
if not logJson["scope"].getStr.startsWith(namespace.get): continue if not logJson["scope"].getStr.startsWith(namespace.get): continue
stdout.writeLine(prettyPrintFormat(logJson)) if compact: stdout.writeLine(compactFormat(logJson))
else: stdout.writeLine(fullFormat(logJson))
except ValueError, JsonParsingError: except ValueError, JsonParsingError:
stdout.writeLine(line) stdout.writeLine(line)
except: except: