@@ -2,8 +2,8 @@ using Dates
22import Base: isless
33
44raw """
5- DatetimeRotatingFileLogger(dir, file_pattern; always_flush=true)
6- DatetimeRotatingFileLogger(f::Function, dir, file_pattern; always_flush=true)
5+ DatetimeRotatingFileLogger(dir, file_pattern; always_flush=true, callback=nothing )
6+ DatetimeRotatingFileLogger(f::Function, dir, file_pattern; always_flush=true, callback=nothing )
77
88Construct a `DatetimeRotatingFileLogger` that rotates its file based on the current date.
99The constructor takes a log output directory, `dir`, and a filename pattern.
@@ -18,6 +18,11 @@ where `log_args` has the following fields:
1818`(level, message, _module, group, id, file, line, kwargs)`.
1919See `?LoggingExtra.handle_message_args` for more information about what each field represents.
2020
21+ It is also possible to pass `callback::Function` as a keyword argument. This function
22+ will be called every time a file rotation is happening. The function should accept one
23+ argument which is the absolute path to the just-rotated file. The logger will block until
24+ the callback function returns. Use `@async` if the callback is expensive.
25+
2126# Examples
2227
2328```julia
@@ -28,19 +33,25 @@ logger = DatetimeRotatingFileLogger(log_dir, raw"\a\c\c\e\s\s-yyyy-mm-dd.\l\o\g"
2833logger = DatetimeRotatingFileLogger(log_dir, raw"yyyy-mm-dd-HH.\l\o\g ") do io, args
2934 println(io, args.level, " | ", args.message)
3035end
36+
37+ # Example callback function to compress the recently-closed file
38+ callback(file) = run(`gzip $(file)`)
3139"""
3240mutable struct DatetimeRotatingFileLogger <: AbstractLogger
3341 logger:: Union{SimpleLogger,FormatLogger}
3442 dir:: String
3543 filename_pattern:: DateFormat
3644 next_reopen_check:: DateTime
3745 always_flush:: Bool
46+ reopen_lock:: ReentrantLock
47+ current_file:: Union{String,Nothing}
48+ callback:: Union{Function,Nothing}
3849end
3950
40- function DatetimeRotatingFileLogger (dir, filename_pattern; always_flush= true )
41- DatetimeRotatingFileLogger (nothing , dir, filename_pattern; always_flush= always_flush)
51+ function DatetimeRotatingFileLogger (dir, filename_pattern; always_flush= true , callback = nothing )
52+ DatetimeRotatingFileLogger (nothing , dir, filename_pattern; always_flush= always_flush, callback = callback )
4253end
43- function DatetimeRotatingFileLogger (f:: Union{Function,Nothing} , dir, filename_pattern; always_flush= true )
54+ function DatetimeRotatingFileLogger (f:: Union{Function,Nothing} , dir, filename_pattern; always_flush= true , callback = nothing )
4455 # Construct the backing logger with a temp IOBuffer that will be replaced
4556 # by the correct filestream in the call to reopen! below
4657 logger = if f === nothing
@@ -50,15 +61,25 @@ function DatetimeRotatingFileLogger(f::Union{Function,Nothing}, dir, filename_pa
5061 end
5162 # abspath in case user constructs the logger with a relative path and later cd's.
5263 drfl = DatetimeRotatingFileLogger (logger, abspath (dir),
53- DateFormat (filename_pattern), now (), always_flush)
64+ DateFormat (filename_pattern), now (), always_flush, ReentrantLock (), nothing , callback )
5465 reopen! (drfl)
5566 return drfl
5667end
5768
5869similar_logger (:: SimpleLogger , io) = SimpleLogger (io, BelowMinLevel)
5970similar_logger (l:: FormatLogger , io) = FormatLogger (l. f, io, l. always_flush)
6071function reopen! (drfl:: DatetimeRotatingFileLogger )
61- io = open (calc_logpath (drfl. dir, drfl. filename_pattern), " a" )
72+ if drfl. current_file != = nothing
73+ # flush and close the old IOStream
74+ flush (drfl. logger. stream)
75+ close (drfl. logger. stream)
76+ if drfl. callback != = nothing
77+ drfl. callback (drfl. current_file)
78+ end
79+ end
80+ new_file = calc_logpath (drfl. dir, drfl. filename_pattern)
81+ drfl. current_file = new_file
82+ io = open (new_file, " a" )
6283 drfl. logger = similar_logger (drfl. logger, io)
6384 drfl. next_reopen_check = next_datetime_transition (drfl. filename_pattern)
6485 return nothing
110131calc_logpath (dir, filename_pattern) = joinpath (dir, Dates. format (now (), filename_pattern))
111132
112133function handle_message (drfl:: DatetimeRotatingFileLogger , args... ; kwargs... )
113- if now () >= drfl. next_reopen_check
114- flush (drfl. logger. stream)
115- reopen! (drfl)
134+ lock (drfl. reopen_lock) do
135+ if now () >= drfl. next_reopen_check
136+ reopen! (drfl)
137+ end
116138 end
117139 handle_message (drfl. logger, args... ; kwargs... )
118140 drfl. always_flush && flush (drfl. logger. stream)
0 commit comments