Skip to content

Commit 3a1140a

Browse files
committed
=bring it all together
1 parent 6aeff18 commit 3a1140a

File tree

7 files changed

+205
-10
lines changed

7 files changed

+205
-10
lines changed

README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,115 @@
33
[![Build Status](https://travis-ci.org/oxinabox/LoggingExtras.jl.svg?branch=master)](https://travis-ci.org/oxinabox/LoggingExtras.jl)
44

55
[![codecov.io](http://codecov.io/github/oxinabox/LoggingExtras.jl/coverage.svg?branch=master)](http://codecov.io/github/oxinabox/LoggingExtras.jl?branch=master)
6+
7+
8+
9+
## Usage
10+
Load the package with `using LoggingExtras`.
11+
You likely also want to load the `Logging` standard lib.
12+
Loggers can be constructed and used like normal.
13+
14+
15+
### Basics of working with loggers
16+
For full details, see the [Julia documentation on Logging](https://docs.julialang.org/en/v1/stdlib/Logging/index.html)
17+
18+
To use a `logger` in a given scope do
19+
```
20+
with_logger(logger) do
21+
#things
22+
end
23+
```
24+
25+
To make a logger the global logger, use
26+
```
27+
global_logger(logger)
28+
```
29+
30+
to get the current global logger, use
31+
```
32+
logger = global_logger()
33+
```
34+
35+
# Loggers introduced by this package:
36+
37+
## `DemuxLogger` and `FileLogger`
38+
39+
The `DemuxLogger` sends the log messages to multiple places.
40+
It takes a list of loggers.
41+
It also has the keyword argument `include_current_global`,
42+
to determine if you also want to log to the global logger.
43+
44+
It is up to those loggers to determine if they will accept it.\
45+
Which they do using their methods for `shouldlog` and `min_enabled_level`.
46+
Or you can do, by wrapping them in a `FilteredLogger` as discussed below.
47+
48+
The `FileLogger` does logging to file.
49+
It is really simple.
50+
It takes a filename; and the minimum level it should log.
51+
52+
### Demo
53+
We are going to log info and above to one file,
54+
and warnings and above to another.
55+
56+
```
57+
julia> using Logging; using LoggingExtras;
58+
59+
julia> demux_logger = DemuxLogger(
60+
FileLogger("info.log", min_level=Logging.Info),
61+
FileLogger("warn.log", min_level=Logging.Warn),
62+
include_current_global=false
63+
);
64+
65+
66+
julia> with_logger(demux_logger) do
67+
@warn("It is bad")
68+
@info("normal stuff")
69+
@error("THE WORSE THING")
70+
@debug("it is chill")
71+
end
72+
73+
shell> cat warn.log
74+
┌ Warning: It is bad
75+
└ @ Main REPL[34]:2
76+
┌ Error: THE WORSE THING
77+
└ @ Main REPL[34]:4
78+
79+
shell> cat info.log
80+
┌ Warning: It is bad
81+
└ @ Main REPL[34]:2
82+
┌ Info: normal stuff
83+
└ @ Main REPL[34]:3
84+
┌ Error: THE WORSE THING
85+
└ @ Main REPL[34]:4
86+
```
87+
88+
## `FilteredLogger`
89+
90+
The `FilteredLogger` exists to give more control over which messages should be logged.
91+
It warps any logger, and before sending messages to the logger to log,
92+
checks them against a filter function.
93+
The filter function takes the full set of parameters of the message.
94+
(See it's docstring with `?FilteredLogger` for more details.)
95+
96+
### Demo
97+
We want to filter to only log strings staring with `"Yo Dawg!"`.
98+
99+
```
100+
julia> function yodawg_filter(level, message, _module, group, id, file, line; kwargs...)
101+
startswith(msg, "Yo Dawg!")
102+
end
103+
yodawg_filter (generic function with 1 method)
104+
105+
julia> filtered_logger = FilteredLogger(yodawg_filter, global_logger());
106+
107+
julia> with_logger(filtered_logger) do
108+
@info "Boring message"
109+
@warn "Yo Dawg! it is bad"
110+
@info "Another boring message"
111+
@info "Yo Dawg! it is all good"
112+
end
113+
┌ Warning: Yo Dawg! it is bad
114+
└ @ Main REPL[28]:3
115+
[ Info: Yo Dawg! it is all good
116+
```
117+

src/LoggingExtras.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Base.CoreLogging:
99
handle_message, shouldlog, min_enabled_level, catch_exceptions
1010

1111
export demux_global_logger,
12-
DemuxLogger, FilterLogger, FileLogger
12+
DemuxLogger, FilteredLogger, FileLogger
1313

1414
include("demuxlogger.jl")
1515
include("filteredlogger.jl")

src/demuxlogger.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ end
2323

2424
function handle_message(demux::DemuxLogger, level, message, _module, group, id, file, line; kwargs...)
2525
for logger in demux.loggers
26-
if shouldlog(logger, level, _module, group, id)
26+
if min_enabled_level(logger)<= level && shouldlog(logger, level, _module, group, id)
27+
# we bypassed those checks above, so we got to check them for each
2728
handle_message(logger, level, message, _module, group, id, file, line; kwargs...)
2829
end
2930
end

src/filelogger.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ struct FileLogger <: AbstractLogger
77
always_flush::Bool
88
end
99

10-
function FileLogger(path, level=Info; append=false, always_flush=true)
10+
function FileLogger(path::AbstractString; min_level=Info, append=false, always_flush=true)
1111
filehandle = open(path, append ? "a" : "w")
12-
FileLogger(SimpleLogger(filehandle, level), always_flush)
12+
FileLogger(SimpleLogger(filehandle, min_level), always_flush)
1313
end
1414

1515

src/filteredlogger.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ at the cost of a bit of overhead.
1010
The `filter` should be a function that returns a boolean.
1111
`true` if the message should be logged and `false` if not.
1212
It should take as inputs:
13-
`level, message, _module, group, id, file, line; kwargs...)`
13+
`(level, message, _module, group, id, file, line; kwargs...)`
1414
1515
These argument come from the logging macro (@info`, `@warn` etc).
1616
@@ -27,14 +27,14 @@ These argument come from the logging macro (@info`, `@warn` etc).
2727
source location of a log message.
2828
* `kwargs...` any keyword or positional arguments to the logging macro.
2929
"""
30-
struct FilteredLogger{T,F} <: AbstractLogger where T<: AbstractLogger
30+
struct FilteredLogger{T <: AbstractLogger, F} <: AbstractLogger
3131
filter::F
32-
logger::SimpleLogger
32+
logger::T
3333
end
3434

3535

3636
function handle_message(filteredlogger::FilteredLogger, level, message, _module, group, id, file, line; kwargs...)
37-
if filteredlogger.filter(filteredlogger.logger, level, message, _module, group, id, file, line; kwargs...)
37+
if filteredlogger.filter(level, message, _module, group, id, file, line; kwargs...)
3838
handle_message(filteredlogger.logger, level, message, _module, group, id, file, line; kwargs...)
3939
end
4040
end

test/demo.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
using Logging; using LoggingExtras
3+
4+
yodawg_filter(lvl, msg, args...; kwargs...) = startswith(msg, "Yo Dawg!")
5+
filtered_logger = FilteredLogger(yodawg_filter, global_logger())
6+
with_logger(filtered_logger) do
7+
@info "Boring message"
8+
@warn "Yo Dawg! it is bad"
9+
@info "Another boring message"
10+
@info "Yo Dawg! it is all good"
11+
end
12+
13+
14+
15+
16+
demux_logger = DemuxLogger(
17+
FileLogger("info.log", min_level=Logging.Info),
18+
FileLogger("warn.log", min_level=Logging.Warn),
19+
include_current_global=false
20+
)
21+
with_logger(demux_logger) do
22+
@warn("It is bad")
23+
@info("normal stuff")
24+
@error("THE WORSE THING")
25+
@debug("it is chill")
26+
end
27+
28+
; cat warn.log
29+
; cat info.log
30+

test/runtests.jl

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,57 @@
11
using LoggingExtras
22
using Test
3+
using Base.CoreLogging
4+
using Base.CoreLogging: Debug, Info, Warn
35

4-
# write your own tests here
5-
@test 1 == 2
6+
using Test: collect_test_logs, TestLogger
7+
8+
9+
10+
11+
@testset "Demux" begin
12+
testlogger_info = TestLogger(min_level=Info)
13+
testlogger_warn = TestLogger(min_level=Warn)
14+
15+
with_logger(DemuxLogger(testlogger_warn, testlogger_info)) do
16+
@info "info1"
17+
@warn "warn1"
18+
@info "info2"
19+
end
20+
@test length(testlogger_info.logs) == 3
21+
@test length(testlogger_warn.logs) == 1
22+
23+
end
24+
25+
26+
27+
@testset "Filter" begin
28+
testlogger = TestLogger()
29+
yodawg_filter(lvl, msg, args...; kwargs...) = startswith(msg, "Yo Dawg!")
30+
31+
filtered_logger = FilteredLogger(yodawg_filter, testlogger)
32+
33+
with_logger(filtered_logger) do
34+
@info "info1"
35+
@warn "Yo Dawg! It is a warning"
36+
@info "info2"
37+
@info "Yo Dawg! It's all good"
38+
end
39+
@test length(testlogger.logs) == 2
40+
41+
end
42+
43+
44+
@testset "File" begin
45+
mktempdir() do dir
46+
filepath = joinpath(dir, "log")
47+
with_logger(FileLogger(filepath)) do
48+
@info "first"
49+
@warn "second"
50+
@info "third"
51+
end
52+
logtext = String(read(filepath))
53+
@test occursin("first", logtext)
54+
@test occursin("second", logtext)
55+
@test occursin("third", logtext)
56+
end
57+
end

0 commit comments

Comments
 (0)