Skip to content

Commit 861a07f

Browse files
authored
allow customizing logfmt standard keys (#16)
* allow customizing logfmt standard keys * don't define `STANDARD_KEYS` twice * bump project
1 parent 4fa07f0 commit 861a07f

File tree

4 files changed

+84
-19
lines changed

4 files changed

+84
-19
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "LoggingFormats"
22
uuid = "98105f81-4425-4516-93fd-1664fb551ab6"
3-
version = "1.4.0"
3+
version = "1.5.0"
44

55
[deps]
66
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,21 @@ level=error msg="something is wrong" module=Main file="REPL[2]" line=3 group="RE
6868

6969
Similarly to the JSON logger, `LogFmt` handles exceptions specially, by printing errors and stacktraces using `Base.showerror`.
7070

71+
One can also restrict the "standard" keys used in the log message, for example:
72+
73+
```julia
74+
julia> using LoggingFormats, LoggingExtras
75+
76+
julia> with_logger(FormatLogger(LoggingFormats.LogFmt((:level, :message, :file)), stderr)) do
77+
@info "hello, world" extra="bye"
78+
@error "something is wrong"
79+
end
80+
level=info msg="hello, world" file="REPL[5]" extra="bye"
81+
level=error msg="something is wrong" file="REPL[5]"
82+
```
83+
84+
Note here that the `module`, `group`, `id` do not appear, since they weren't specified, but the "custom" key `extra` still appears. The full set of standard keys is `(:level, :msg, :module, :file, :line, :group, :id)`, which are all used by default, in that order.
85+
7186
## `Truncated`: Truncate long variables and messages
7287

7388
`LoggingFormats.Truncated(max_var_len=5_000)` is a function which formats data in similar manner as `ConsoleLogger`,

src/LoggingFormats.jl

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -147,26 +147,60 @@ end
147147
############
148148
# See https://brandur.org/logfmt
149149

150+
"""
151+
LogFmt(standard_keys=$STANDARD_KEYS)
152+
LogFmt(standard_keys...)
153+
154+
Creates a `logfmt` format logger. The log message includes each of the `standard_keys`, as well as any "custom" keys. For example,
155+
156+
```julia
157+
julia> using LoggingFormats, LoggingExtras
158+
159+
julia> with_logger(FormatLogger(LoggingFormats.LogFmt((:level, :message, :file)), stderr)) do
160+
@info "hello, world" extra="bye"
161+
@error "something is wrong"
162+
end
163+
level=info msg="hello, world" file="REPL[5]" extra="bye"
164+
level=error msg="something is wrong" file="REPL[5]"
165+
```
166+
167+
Note that the order of arguments to `LogFmt` is respected in the log printing.
168+
"""
150169
struct LogFmt <: Function
170+
standard_keys::NTuple{<:Any,Symbol}
171+
function LogFmt(keys::NTuple{N,Symbol}) where {N}
172+
extra = setdiff(keys, STANDARD_KEYS)
173+
174+
if !isempty(extra)
175+
if length(extra) == 1
176+
extra = first(extra)
177+
plural = ""
178+
else
179+
extra = Tuple(extra)
180+
plural = "s"
181+
end
182+
throw(ArgumentError("Unsupported standard logging key$plural `$(repr(extra))` found. The only supported keys are: `$STANDARD_KEYS`."))
183+
end
184+
return new(keys)
185+
end
151186
end
152-
function (::LogFmt)(io, args)
153-
print(io, "level=", lvlstr(args.level),
154-
" msg=\"",
155-
)
156-
escape_string(io, args.message isa AbstractString ? args.message : string(args.message), '"')
157-
print(io, "\"",
158-
" module=", something(args._module, "nothing"),
159-
" file=\"",
160-
)
161-
escape_string(io, args.file isa AbstractString ? args.file : string(something(args.file, "nothing")), '"')
162-
print(io, "\"",
163-
" line=", something(args.line, "nothing"),
164-
" group=\"",
165-
)
166-
escape_string(io, args.group isa AbstractString ? args.group : string(something(args.group, "nothing")), '"')
167-
print(io, "\"",
168-
" id=", something(args.id, "nothing"),
169-
)
187+
LogFmt() = LogFmt(STANDARD_KEYS)
188+
LogFmt(keys::Symbol...) = LogFmt(keys)
189+
190+
function fmtval(k, v)
191+
k == :level && return lvlstr(v)
192+
return v isa AbstractString ? v : string(something(v, "nothing"))
193+
end
194+
195+
function (l::LogFmt)(io, args)
196+
for (i, k) in enumerate(l.standard_keys)
197+
i == 1 || print(io, ' ')
198+
print(io, k, '=')
199+
k in (:level, :module) || print(io, '"')
200+
k_lookup = k === :module ? :_module : k === :msg ? :message : k
201+
escape_string(io, fmtval(k, getproperty(args, k_lookup)), '"')
202+
k in (:level, :module) || print(io, '"')
203+
end
170204
for (k, v) in args.kwargs
171205
print(io, " ", k, "=\"")
172206
v = maybe_stringify_exceptions(v)

test/runtests.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,22 @@ end
207207
end
208208

209209
@testset "logfmt" begin
210+
# Unsupported keys:
211+
@test_throws ArgumentError("Unsupported standard logging key `:hi` found. The only supported keys are: `(:level, :msg, :module, :file, :line, :group, :id)`.") LogFmt((:hi,))
212+
@test_throws ArgumentError("Unsupported standard logging keys `(:hi, :bye)` found. The only supported keys are: `(:level, :msg, :module, :file, :line, :group, :id)`.") LogFmt((:hi, :bye))
213+
@test_throws MethodError LogFmt("no")
214+
215+
# Fewer keys, out of order
216+
io = IOBuffer()
217+
with_logger(FormatLogger(LogFmt(:msg, :level, :file), io)) do
218+
@debug "debug msg" extra="hi"
219+
@info "info msg" _file="file with space.jl"
220+
end
221+
strs = collect(eachline(seekstart(io)))
222+
@test match(r"msg=\"debug msg\" level=debug file=\"(.*)\" extra=\"hi\"", strs[1]) !== nothing
223+
@test strs[2] == "msg=\"info msg\" level=info file=\"file with space.jl\""
224+
225+
# Standard:
210226
io = IOBuffer()
211227
with_logger(FormatLogger(LogFmt(), io)) do
212228
@debug "debug msg"

0 commit comments

Comments
 (0)