-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow AnnotatedStrings in log messages #51802
Conversation
I'm not sure that this actually does need any additional tests, without StyledStrings loaded there won't be any change in behavior. Perhaps it would be nice to separately test the "with styled strings loaded" and "without styled strings loaded" cases, but I'm not sure how that could be done well, and whether it would be worth it. |
stdlib/Logging/src/ConsoleLogger.jl
Outdated
@@ -116,7 +116,13 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module | |||
|
|||
# Generate a text representation of the message and all key value pairs, | |||
# split into lines. | |||
msglines = [(indent=0, msg=l) for l in split(chomp(convert(String, string(message))::String), '\n')] | |||
msglines = if message isa Base.AnnotatedString |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels a bit like a design failure that any custom string type needs to be hard coded here, but maybe it is unavoidable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The specialisation on String
not AbstractString
is largely unnoticeable, it just so happens that with an AnnotatedString
(and only AnnotatedString
, as far as I'm aware), there's a potential difference in the externally visible behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fredrikekre do please let me know if you've got any further comments here, otherwise I'll just mark this as resolved shortly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree this seems odd. The assert and convert here was added by #38292 and #44500 in the latency hunts; without them there'd be no need for a separate codepath — and the separate codepath is what makes this want for tests.
But I suppose going back to the simple [(indent=0, msg=l) for l in split(chomp(string(message)), '\n')]
would likely reintroduce invalidations. Maybe worth a check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe? I'm not sure I'd be a good person to check however — perhaps someone more familiar with invalidations could take a look if needed?
If this is indeed a little tricky to work out, might it be possible to merge this as an initial 1 -> 2 AbstractString
widening of the allowed types here, and then explore making this fully generic again in a subsequent PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I'm not entirely sure of this, but I think the following might suggest that a widening to "any AbstractString
" may be premature given Tim's invalidation concerns.
# What it used to be before Tim's PRs
julia> m0(message) = [(indent=0, msg=l) for l in split(chomp(string(message)), '\n')]
m0 (generic function with 1 method)
# The current code
julia> m1(message) = [(indent=0, msg=l) for l in split(chomp(convert(String, string(message))::String), '\n')]
m1 (generic function with 1 method)
# The current PR
julia> m2(message) = if message isa Base.AnnotatedString
NamedTuple{(:indent, :msg), Tuple{Int, SubString{<:Union{typeof(message), String}}}}[
(indent=0, msg=l) for l in split(chomp(message), '\n')]
else
[(indent=0, msg=l) for l in split(
chomp(convert(String, string(message))::String), '\n')]
end
m2 (generic function with 1 method)
# What I'm about to push
julia> m3(message) = if message isa Base.AnnotatedString
message = Base.AnnotatedString(String(message), Base.annotations(message))
NamedTuple{(:indent, :msg), Tuple{Int, SubString{<:Union{Base.AnnotatedString{String}, String}}}}[
(indent=0, msg=l) for l in split(chomp(message), '\n')]
else
[(indent=0, msg=l) for l in split(
chomp(convert(String, string(message))::String), '\n')]
end
m3 (generic function with 1 method)
julia> Base.return_types(m0, (Any,))
1-element Vector{Any}:
Vector (alias for Array{T, 1} where T)
julia> Base.return_types(m1, (Any,))
1-element Vector{Any}:
Vector{@NamedTuple{indent::Int64, msg::SubString{String}}} (alias for Array{@NamedTuple{indent::Int64, msg::SubString{String}}, 1})
julia> Base.return_types(m2, (Any,))
1-element Vector{Any}:
Array{NamedTuple{(:indent, :msg), var"#s178"}, 1} where var"#s178"<:Tuple{Int64, Vararg}
julia> Base.return_types(m3, (Any,))
1-element Vector{Any}:
Union{Vector{@NamedTuple{indent::Int64, msg::SubString{<:Union{Base.AnnotatedString{String}, String}}}}, Vector{@NamedTuple{indent::Int64, msg::SubString{String}}}}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(message
has no type restriction in the signature of handle_message
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and the separate codepath is what makes this want for tests.
Added a test for the separate codepath.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handle_message
shifted to being called via invokelatest
(the "nuclear option" when it comes to eliminating invalidations) in #47889; any changes that occurred prior to that PR may not be required anymore. If you want to be sure but don't feel comfortable using SnoopCompile, just (1) start Julia with --trace-compile
, (2) define a function that uses logging and run it to force compilation, (3) load InlineStrings, (4) run it again and see if your function got recompiled.
It would be great if somebody with merge rights could spare the time to have a look at this PR. |
f88021d
to
4cabfcc
Compare
d4f2c3a
to
c766cbf
Compare
stdlib/Logging/src/ConsoleLogger.jl
Outdated
message = string(message) | ||
msglines = if Base._isannotated(message) | ||
message = Base.AnnotatedString(String(message), Base.annotations(message)) | ||
@NamedTuple{indent::Int, msg::SubString{<:Union{Base.AnnotatedString{String}, String}}}[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@NamedTuple{indent::Int, msg::SubString{<:Union{Base.AnnotatedString{String}, String}}}[ | |
@NamedTuple{indent::Int, msg::SubString{Base.AnnotatedString{String}}[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is invalid since later code pushes values like (indent=2, msg=SubString("$key ="))
to msglines
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That shouldn't be invalid, if the convert methods are defined correctly.
But anyways, that suggest this should have the same type on both branches then, so you should update the other branch as well to have this type. I think you can slightly simplify it by making it a pair of concrete type also, instead of a UnionAll type (though this indeed would break the convert method handling if something isn't already a SubString of one of these 2 kinds, since we cannot convert to a Union).
@NamedTuple{indent::Int, msg::SubString{<:Union{Base.AnnotatedString{String}, String}}}[ | |
@NamedTuple{indent::Int, msg::Union{SubString{Base.AnnotatedString{String}}, SubString{String}}}}[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh? I recall widening this type because of an issue I ran into when developing this PR, but October last year is too far back for me to recall what was happening clearly.
I've applied the concrete-ification you've suggested.
stdlib/Logging/src/ConsoleLogger.jl
Outdated
message = string(message) | ||
msglines = if Base._isannotated(message) | ||
message = Base.AnnotatedString(String(message), Base.annotations(message)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, seems right to me. I think you can also combine a few more code paths like this also
message = string(message) | |
msglines = if Base._isannotated(message) | |
message = Base.AnnotatedString(String(message), Base.annotations(message)) | |
message = string(message) | |
messagestr = String(message)::String | |
annots = Base._isannotated(message) | |
messageattrs = annots ? Base.annotations(message) : () | |
msglines = if annots && !isempty(messageattrs) | |
message = Base.AnnotatedString(messagestr, messageattrs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't help but feel this is taking DRY too far...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess mainly it is ugly since I am taking advantage of the DRY-er code to have it use the non-annotated code path if it detects there are no relevant annotations on this (sub)string. I don't know if it is necessarily worth doing that, as it does force the code to be somewhat more linear here (longer) as each line cannot do multiple computations anymore (even just annotations
and String
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the push I recently did, I added that check too by just doing if Base._isannotated(message) && !isempty(Base.annotations(message))
. FWWIW, I think the main visual impediment with the current code isn't the fact it could be slightly DRY-er but the Base.
prefixes. Without them, this is the current code:
message = string(message)
msglines = if _isannotated(message) && !isempty(annotations(message))
message = AnnotatedString(String(message), annotations(message))
@NamedTuple{indent::Int, msg::Union{SubString{AnnotatedString{String}}, SubString{String}}}[
(indent=0, msg=l) for l in split(chomp(message), '\n')]
else
[(indent=0, msg=l) for l in split(
chomp(convert(String, message)::String), '\n')]
end
c766cbf
to
904d6da
Compare
Permitting annotated strings allows for styling information to be preserved through to log printing.
904d6da
to
fe99e9a
Compare
Ok, the only test failure seems to be a bootstrap error with the Mingw32 windows build, everything else in CI looks happy. |
Permitting annotated strings allows for styling information to be preserved through to log printing.
Permitting annotated strings allows for styling information to be preserved through to log printing. (cherry picked from commit 1998518)
Backported PRs: - [x] #39071 <!-- Add a lazy `logrange` function and `LogRange` type --> - [x] #51802 <!-- Allow AnnotatedStrings in log messages --> - [x] #53369 <!-- Orthogonalize re-indexing for FastSubArrays --> - [x] #48050 <!-- improve `--heap-size-hint` arg handling --> - [x] #53482 <!-- add IR encoding for EnterNode --> - [x] #53499 <!-- Avoid compiler warning about redefining jl_globalref_t --> - [x] #53507 <!-- update staled `Core.Compiler.Effects` documentation --> - [x] #53408 <!-- task splitting: change additive accumulation to multiplicative --> - [x] #53523 <!-- add back an alias for `check_top_bit` --> - [x] #53377 <!-- add _readdirx for returning more object info gathered during dir scan --> - [x] #53525 <!-- fix InteractiveUtils call in Base.runtests on failure --> - [x] #53540 <!-- use more efficient `_readdirx` for tab completion --> - [x] #53545 <!-- use `_readdirx` for `walkdir` --> - [x] #53551 <!-- revert "Add @create_log_macro for making custom styled logging macros (#52196)" --> - [x] #53554 <!-- Always return a value in 1-d circshift! of abstractarray.jl --> - [x] #53424 <!-- yet more atomics & cache-line fixes on work-stealing queue --> - [x] #53571 <!-- Update Documenter to v1.3 for inventory writing --> - [x] #53403 <!-- Move parallel precompilation to Base --> - [x] #53589 <!-- add back `unsafe_convert` to pointer for arrays --> - [x] #53596 <!-- build: remove extra .a file --> - [x] #53606 <!-- fix error path in `precompilepkgs` --> - [x] #53004 <!-- Unexport with, at_with, and ScopedValue from Base --> - [x] #53629 <!-- typo fix in scoped values docs --> - [x] #53630 <!-- sroa: Fix incorrect scope counting --> - [x] #53598 <!-- Use Base parallel precompilation to build stdlibs --> - [x] #53649 <!-- precompilepkgs: package in boths deps and weakdeps are in fact only weak --> - [x] #53671 <!-- Fix bootstrap Base precompile in cross compile configuration --> - [x] #52125 <!-- Load Pkg if not already to reinstate missing package add prompt --> - [x] #53602 <!-- Handle zero on arrays of unions of number types and missings --> - [x] #53516 <!-- permit NamedTuple{<:Any, Union{}} to be created --> - [x] #53643 <!-- Bump CSL to 1.1.1 to fix libgomp bug --> - [x] #53679 <!-- move precompile workload back from Base --> - [x] #53663 <!-- add isassigned methods for reinterpretarray --> - [x] #53662 <!-- [REPL] fix incorrectly cleared line after completions accepted --> - [x] #53611 <!-- Linalg: matprod_dest for Diagonal and adjvec --> - [x] #53659 <!-- fix #52025, re-allow all implicit pointer casts in cconvert for Array --> - [x] #53631 <!-- LAPACK: validate input parameters to throw informative errors --> - [x] #53628 <!-- Make some improvements to the Scoped Values documentation. --> - [x] #53655 <!-- Change tbaa of ptr_phi to tbaa_value --> - [x] #53391 <!-- Default to the medium code model in x86 linux --> - [x] #53699 <!-- Move `isexecutable, isreadable, iswritable` to `filesystem.jl` --> - [x] #41232 <!-- Fix linear indexing for ReshapedArray if the parent has offset axes --> - [x] #53527 <!-- Enable analyzegc checks for try catch and fix found issues --> - [x] #52092 - [x] #53682 <!-- Increase build precompilation --> - [x] #53720 - [x] #53553 <!-- typeintersect: fix `UnionAll` unaliasing bug caused by innervars. --> Contains multiple commits, manual intervention needed: - [ ] #53305 <!-- Propagate inbounds in isassigned with CartesianIndex indices --> Non-merged PRs with backport label: - [ ] #53736 <!-- fix literal-pow to return the right type when the base is -1 --> - [ ] #53707 <!-- Make ScopedValue public --> - [ ] #53696 <!-- add invokelatest to on_done callback in bracketed paste --> - [ ] #53660 <!-- put Logging back in default sysimage --> - [ ] #53509 <!-- revert moving "creating packages" from Pkg.jl --> - [ ] #53452 <!-- RFC: allow Tuple{Union{}}, returning Union{} --> - [ ] #53402 <!-- Add `jl_getaffinity` and `jl_setaffinity` --> - [ ] #52694 <!-- Reinstate similar for AbstractQ for backward compatibility --> - [ ] #51928 <!-- Styled markdown, with a few tweaks --> - [ ] #51816 <!-- User-themable stacktraces --> - [ ] #51811 <!-- Make banner size depend on terminal size --> - [ ] #51479 <!-- prevent code loading from lookin in the versioned environment when building Julia -->
Permitting annotated strings allows for styling information to be preserved through to log printing.