Skip to content

Commit

Permalink
Threadsafe TerminalLogger (#41)
Browse files Browse the repository at this point in the history
Added a big lock around TerminalLogger state to make it safe to use
from multiple threads.

Also
* Bump version
* Fix tests which are now broken on julia 1.6 due to formatting detail
  of Matrix.
* Update CI to run on more recent Julia versions
  • Loading branch information
c42f authored Nov 4, 2021
1 parent 9ed38e2 commit 037403c
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 28 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ jobs:
matrix:
version:
- '1.0'
- '1.3'
- '1.6'
- '1'
- 'nightly'
os:
- ubuntu-latest
- macOS-latest
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "TerminalLoggers"
uuid = "5d786b92-1e48-4d6f-9151-6b4477ca9bed"
authors = ["Chris Foster <[email protected]>"]
version = "0.1.4"
version = "0.1.5"

[deps]
LeftChildRightSiblingTrees = "1d6d02ad-be62-4b6b-8a6d-2f90e265016e"
Expand Down
45 changes: 29 additions & 16 deletions src/TerminalLogger.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ struct TerminalLogger <: AbstractLogger
message_limits::Dict{Any,Int}
sticky_messages::StickyMessages
bartrees::Vector{Node{ProgressBar}}
lock::ReentrantLock
end
function TerminalLogger(stream::IO=stderr, min_level=ProgressLevel;
meta_formatter=default_metafmt, show_limited=true,
Expand All @@ -49,11 +50,15 @@ function TerminalLogger(stream::IO=stderr, min_level=ProgressLevel;
Dict{Any,Int}(),
StickyMessages(stream),
Union{}[],
ReentrantLock()
)
end

shouldlog(logger::TerminalLogger, level, _module, group, id) =
get(logger.message_limits, id, 1) > 0
function shouldlog(logger::TerminalLogger, level, _module, group, id)
lock(logger.lock) do
get(logger.message_limits, id, 1) > 0
end
end

min_enabled_level(logger::TerminalLogger) = logger.min_level

Expand Down Expand Up @@ -280,14 +285,19 @@ function handle_message(logger::TerminalLogger, level, message, _module, group,
filepath, line; maxlog=nothing,
sticky=nothing, kwargs...)
if maxlog !== nothing && maxlog isa Integer
remaining = get!(logger.message_limits, id, maxlog)
logger.message_limits[id] = remaining - 1
remaining = 0
lock(logger.lock) do
remaining = get!(logger.message_limits, id, maxlog)
logger.message_limits[id] = remaining - 1
end
remaining > 0 || return
end

progress = asprogress(level, message, _module, group, id, filepath, line; kwargs...)
if progress !== nothing
handle_progress(logger, progress, kwargs)
lock(logger.lock) do
handle_progress(logger, progress, kwargs)
end
return
end

Expand Down Expand Up @@ -343,18 +353,21 @@ function handle_message(logger::TerminalLogger, level, message, _module, group,
end

msg = take!(buf)
if sticky !== nothing
# Ensure we see the last message, even if it's :done
push!(logger.sticky_messages, id=>String(msg))
if sticky == :done
pop!(logger.sticky_messages, id)

lock(logger.lock) do
if sticky !== nothing
# Ensure we see the last message, even if it's :done
push!(logger.sticky_messages, id=>String(msg))
if sticky == :done
pop!(logger.sticky_messages, id)
end
else
write(logger.stream, msg)
end

if logger.always_flush
flush(logger.stream)
end
else
write(logger.stream, msg)
end

if logger.always_flush
flush(logger.stream)
end

nothing
Expand Down
57 changes: 48 additions & 9 deletions test/TerminalLogger.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using TerminalLoggers: default_metafmt, format_message

using ProgressLogging

@noinline func1() = backtrace()

function dummy_metafmt(level, _module, group, id, file, line)
Expand Down Expand Up @@ -192,8 +194,8 @@ end
┌ PREFIX msg
│ exception =
│ DivideError: integer division error
│ Stacktrace:
[1] func1() at""")
│ Stacktrace:""")
@test occursin("[1] func1", genmsg("msg", exception=(DivideError(),bt)))

# Exception stacks
if VERSION >= v"1.2"
Expand All @@ -211,19 +213,21 @@ end
end

@testset "Limiting large data structures" begin
@test genmsg("msg", a=fill(1.00001, 100,100), b=fill(2.00002, 10,10)) ==
a = fill(1.00001, 10,10)
b = fill(2.00002, 10,10)
@test genmsg("msg", a=a, b=b) ==
replace("""
┌ PREFIX msg
│ a =
100×100 Array{Float64,2}:
$(summary(a)):
│ 1.00001 1.00001 1.00001 1.00001 … 1.00001 1.00001 1.00001
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
│ ⋮ ⋱ EOL
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
│ b =
10×10 Array{Float64,2}:
$(summary(b)):
│ 2.00002 2.00002 2.00002 2.00002 … 2.00002 2.00002 2.00002
│ 2.00002 2.00002 2.00002 2.00002 2.00002 2.00002 2.00002
│ 2.00002 2.00002 2.00002 2.00002 2.00002 2.00002 2.00002
Expand All @@ -237,11 +241,11 @@ end
(VERSION < v"1.4-" ? "EOL" : " EOL")=>""
)
# Limiting the amount which is printed
@test genmsg("msg", a=fill(1.00001, 10,10), show_limited=false) ==
@test genmsg("msg", a=a, show_limited=false) ==
"""
┌ PREFIX msg
│ a =
10×10 Array{Float64,2}:
$(summary(a)):
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
Expand Down Expand Up @@ -300,9 +304,8 @@ end

# For non-strings a blank line is added so that any formatting for
# vertical alignment isn't broken
@test format_message([1 2; 3 4], 6, io_ctx) ==
@test format_message(Text(" 1 2\n 3 4"), 6, io_ctx) ==
["",
"2×2 Array{Int64,2}:",
" 1 2",
" 3 4"]
end
Expand Down Expand Up @@ -393,4 +396,40 @@ end
Outer 100%\|█+\| Time: .*
"""
end

@static if VERSION >= v"1.3.0"
@testset "Parallel progress" begin
buf = IOBuffer()
io = IOContext(buf, :displaysize=>(30,75), :color=>false)
logger = TerminalLogger(io, Debug)
# Crude multithreading test: generate some contention.
#
# Generate some contention in multi-threaded cases
ntasks = 8
@sync begin
with_logger(logger) do
for i=1:ntasks
Threads.@spawn for j=1:100
@info "XXXX <$i,$j>" maxlog=100
#sleep(0.001)
end
end
end
end
log_str = String(take!(buf))
@test length(findall("XXXX", log_str)) == 100

# Fun test of parallel progress logging to watch interactively:
#=
using ProgressLogging
@sync begin
for i=1:8
Threads.@spawn @progress name="task $i" threshold=0.00005 for j=1:10000
#sleep(0.001)
end
end
end
=#
end
end
end
3 changes: 2 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ using Test

using Logging:
LogLevel, BelowMinLevel, Debug, Info, Warn, Error, AboveMaxLevel,
shouldlog, handle_message, min_enabled_level, catch_exceptions
shouldlog, handle_message, min_enabled_level, catch_exceptions,
with_logger

using ProgressLogging: Progress
using UUIDs: UUID
Expand Down

2 comments on commit 037403c

@c42f
Copy link
Member Author

@c42f c42f commented on 037403c Nov 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/48183

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.1.5 -m "<description of version>" 037403cb5e30295b3bc12f47d405586217885873
git push origin v0.1.5

Please sign in to comment.