Skip to content

Commit

Permalink
feat: Made LogRecordData keys parametric, rather than being restric…
Browse files Browse the repository at this point in the history
…ted to `Symbol`s. (#5)

`LogRecordData` is now backed by a `Dictionaries.Dictionary`

feat: Added string interning

feat: Added `add_record_data!` method
  • Loading branch information
curtd authored Apr 23, 2024
1 parent 9c1c578 commit c5f7cb1
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 42 deletions.
7 changes: 7 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@ version = "1.0.2"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4"
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
ForwardMethods = "5fe2550f-d27e-4649-9aea-fdf9a83a1aa9"
InternedStrings = "7d512f48-7fb1-5a58-b986-67e6dc259f01"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"

[compat]
Dates = "1"
Dictionaries = "0.3, 0.4"
Distributed = "1"
ForwardMethods = "1.6"
InternedStrings = "0.7"
Logging = "1"
PrecompileTools = "1"
julia = "1.6"
4 changes: 2 additions & 2 deletions src/LoggingCommon.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module LoggingCommon

using Dates, Dictionaries, Distributed, Logging, PrecompileTools
using Dates, Dictionaries, Distributed, ForwardMethods, Logging, InternedStrings, PrecompileTools

# Log levels
export NotSet, All, Trace,Notice, Critical, Alert, Emergency, Fatal, AboveMax, Off
Expand All @@ -19,7 +19,7 @@ module LoggingCommon

export AbstractLogRecord, LogRecord, MessageLogRecord, StacktraceLogRecord

export message_log_record, stacktrace_log_record
export message_log_record, stacktrace_log_record, add_record_data!

export log_record_data, static_metadata, runtime_metadata, is_error_record

Expand Down
2 changes: 1 addition & 1 deletion src/levels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ end

NamedLogLevel(l::NamedLogLevel) = l

Base.string(l::NamedLogLevel) = string(l.name)
Base.string(l::NamedLogLevel) = intern(string(l.name))

log_level(l::NamedLogLevel) = symbol_to_log_levels[l.name]

Expand Down
89 changes: 60 additions & 29 deletions src/records.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function module_str_trim_main(_module::Module)
name = fullname(_module)
if first(name) == :Main && length(name) > 1
return join(name[2:end], '.')
return join(view(name, 2:length(name)), '.')
else
return join(name, '.')
end
Expand Down Expand Up @@ -31,13 +31,14 @@ function StaticLogRecordMetadata(source::AbstractString, level::LogLevel, level_
return StaticLogRecordMetadata(string(source), level, level_name, something(filename, "?"), line_num, group, id)
end

StaticLogRecordMetadata(source::AbstractString, level, lnn::LineNumberNode, args...) = StaticLogRecordMetadata(source, level, "", _filename(lnn), lnn.line, args...)
StaticLogRecordMetadata(source::AbstractString, level::LogLevel, lnn::LineNumberNode, args...) = StaticLogRecordMetadata(source, level, intern(""), _filename(lnn), lnn.line, args...)

StaticLogRecordMetadata(source::AbstractString, level::LogLevel, filename::Union{LineNumberNode, String, Nothing}, line_num::Union{LineNumberNode, Int}, args...) = StaticLogRecordMetadata(source, level, string(nearest_log_level(level)), filename, line_num, args...)
StaticLogRecordMetadata(source::AbstractString, level::LogLevel, filename::Union{String, Nothing}, line_num::Union{LineNumberNode, Int}, args...) = StaticLogRecordMetadata(source, level, string(nearest_log_level(level)), filename, line_num, args...)

StaticLogRecordMetadata(source::AbstractString, level::NamedLogLevel, args...) = StaticLogRecordMetadata(source, log_level(level), string(level), args...)

StaticLogRecordMetadata(source::Module, args...) = StaticLogRecordMetadata(module_str_trim_main(source), args...)

StaticLogRecordMetadata(source::Module, args...) = StaticLogRecordMetadata(intern(module_str_trim_main(source)), args...)

"""
RuntimeLogRecordMetadata(datetime::DateTime, thread_id::Int, worker_id::Int)
Expand All @@ -59,34 +60,61 @@ end
"""
LogRecordData(data)
LogRecordData(args::Pair{Symbol, <:Any}...)
LogRecordData(args::Pair{<:Any, <:Any}...)
A type representing an optional collection of `key => value` pairs attached to a log record.
A type representing an optional collection of `key => value` pairs attached to a log record.
`data` must be an iterable collection where each element is a `Pair`
"""
struct LogRecordData
data::Union{Nothing,Vector{Pair{Symbol, Any}}}
function LogRecordData(d; exclude::Union{Symbol,Vector{Symbol}}=Symbol[])
if !isnothing(d) && !isempty(d)
_exclude = exclude isa Symbol ? [exclude] : exclude
return new([convert(Pair{Symbol,Any}, di) for di in d if first(di) _exclude])
else
return new(nothing)
end
end
struct LogRecordData{K}
data::Dictionary{K, Any}
LogRecordData{K}() where {K} = new(Dictionary{K, Any}())
end
Base.isempty(l::LogRecordData) = isnothing(l.data) || isempty(l.data)
Base.length(l::LogRecordData) = isnothing(l.data) ? 0 : length(l.data)
Base.pairs(l::LogRecordData) = isnothing(l.data) ? pairs((;)) : l.data
Base.iterate(l::LogRecordData) = isnothing(l.data) ? nothing : iterate(l.data)
Base.iterate(l::LogRecordData, st) = isnothing(st) ? nothing : iterate(l.data, st)

function LogRecordData(args::Pair{Symbol, <:Any}...)
if !isempty(args)
return LogRecordData(collect(args))
else
return LogRecordData(nothing)
@forward_methods LogRecordData field=data Base.isempty(_) Base.length(_) Base.pairs(_)
Base.eltype(::Type{LogRecordData{K}}) where {K} = Pair{K, Any}
Base.iterate(d::LogRecordData) = iterate(pairs(d.data))
Base.iterate(d::LogRecordData, st) = iterate(pairs(d.data), st)
Base.collect(d::LogRecordData) = collect(pairs(d.data))

"""
add_record_data!(r, data::Pair)
Adds the `data := key => value` pair to `r`
"""
add_record_data!(d::LogRecordData{K}, data::Pair{K, <:Any}) where {K} = (set!(d.data, first(data), last(data)); nothing)

add_record_data!(d::LogRecordData{K}, data::Pair) where {K} = add_record_data!(d, convert(K, first(data))::K => last(data))

function _log_record_data(kv_pairs, T; exclude=())
d = LogRecordData{T}()
_exclude = (exclude isa Tuple || exclude isa Vector) ? exclude : (exclude,)
for (k, v) in kv_pairs
if k _exclude
set!(d.data, k, v)
end
end
return d
end
key_type(::Type{Pair{K, V}}) where {K, V} = K

"""
log_record_data(kv_pairs; [exclude=()]) -> LogRecordData
Returns a `LogRecordData` from the input `key => value` pairs
"""
log_record_data(kv_pairs; exclude=()) = _log_record_data(kv_pairs, mapfoldl(key_type typeof, promote_type, kv_pairs; init=Union{}); exclude)

"""
log_record_data() -> LogRecordData{Symbol}
"""
log_record_data() = _log_record_data((), Symbol)

LogRecordData(::Nothing; kwargs...) = _log_record_data((), Symbol; kwargs...)
LogRecordData(data; kwargs...) = log_record_data(data; kwargs...)
LogRecordData(args::Pair{Symbol, <:Any}...; kwargs...) = _log_record_data(args, Symbol; kwargs...)



"""
AbstractLogRecord
Expand All @@ -98,7 +126,7 @@ abstract type AbstractLogRecord end
"""
log_record_data(record)
Returns the `key` => `value` pairs associated with `record`
Returns an iterator over the `key` => `value` pairs associated with `record`
"""
log_record_data(::AbstractLogRecord) = nothing

Expand Down Expand Up @@ -155,6 +183,9 @@ struct LogRecord{R} <: AbstractLogRecord
data::LogRecordData
record::R
end
@forward_methods LogRecord field=data add_record_data!(_, data)

Base.propertynames(::LogRecord{R}) where {R} = (fieldnames(LogRecord)..., fieldnames(R)...)

function Base.getproperty(l::LogRecord, name::Symbol)
if name === :static_meta || name === :runtime_meta || name === :data || name === :record
Expand All @@ -166,7 +197,7 @@ end

LogRecord(static_meta::StaticLogRecordMetadata, runtime_meta::RuntimeLogRecordMetadata, record::AbstractLogRecord, data::LogRecordData) = LogRecord{typeof(record)}(static_meta, runtime_meta, data, record)

LogRecord(static_meta::StaticLogRecordMetadata, runtime_meta::RuntimeLogRecordMetadata, record::AbstractLogRecord, args::Pair{Symbol, <:Any}...) = LogRecord(static_meta, runtime_meta, record, LogRecordData(args...))
LogRecord(static_meta::StaticLogRecordMetadata, runtime_meta::RuntimeLogRecordMetadata, record::AbstractLogRecord, args::Pair{<:Any, <:Any}...) = LogRecord(static_meta, runtime_meta, record, LogRecordData(args...))

LogRecord(static_meta::StaticLogRecordMetadata, record::AbstractLogRecord, args...; runtime_meta::RuntimeLogRecordMetadata=RuntimeLogRecordMetadata()) = LogRecord(static_meta, runtime_meta, record, args...)

Expand Down
6 changes: 5 additions & 1 deletion test/Project.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[deps]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
TestingUtilities = "40452611-1178-4e48-bdfc-3af4bebad9c9"
TestingUtilities = "40452611-1178-4e48-bdfc-3af4bebad9c9"

[compat]
Aqua = "0.8"
23 changes: 14 additions & 9 deletions test/TestLoggingCommon.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,30 +73,30 @@ module TestLoggingCommon
end
@testset "LogRecordData" begin
d = LogRecordData()
@Test isnothing(d.data)
@Test isnothing(Base.iterate(d))
@Test isempty(Base.pairs(d))
@Test isempty(d)
@Test length(d) == 0
d = LogRecordData(:a => 1)
@Test d.data == [:a => 1]
@Test Base.pairs(d) == d.data
@Test Base.iterate(d) == (:a => 1, 2)
@Test Base.iterate(d, 2) |> isnothing
@Test Base.pairs(d) |> collect == [:a => 1]
@Test Base.iterate(d) == (:a => 1, 1)
@Test Base.iterate(d, 1) |> isnothing
@Test !isempty(d)
@Test length(d) == 1

d = LogRecordData(:a => 1, :b => String)
@Test length(d) == 2
for (i, (k,v)) in enumerate(d)
if i == 1
@test k == :a
@test v == 1
@Test k == :a
@Test v == 1
else
@test k == :b
@test v == String
@Test k == :b
@Test v == String
end
end
d = log_record_data(("a" => 1, "b" => 2); exclude="a")
@Test collect(d) == ["b" => 2]
end
@testset "message_log_record" begin
static = StaticLogRecordMetadata(Main, NamedLogLevel(:info), "a.jl", 1, "group", "id")
Expand All @@ -109,6 +109,11 @@ module TestLoggingCommon
@test !is_error_record(record)
@test isempty(log_record_data(record))

add_record_data!(record, :a => "1")
@Test log_record_data(record) |> collect == [:a => "1"]
add_record_data!(record, :b => true)
@Test log_record_data(record) |> collect == [:a => "1", :b => true]

static = StaticLogRecordMetadata(Main, NamedLogLevel(:error), "a.jl", 1, "group", "id")
record = message_log_record(static, "Error message", :a => 1, :b => String)
@test static_metadata(record) == static
Expand Down
7 changes: 7 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
using Test

using LoggingCommon

if VERSION v"1.9"
using Aqua
Aqua.test_all(LoggingCommon)
end

include("TestLoggingCommon.jl")

0 comments on commit c5f7cb1

Please sign in to comment.