Skip to content
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

add logging() to redirect info/warn/error() #16213

Merged
merged 1 commit into from
Jan 5, 2017

Conversation

bjarthur
Copy link
Contributor

@bjarthur bjarthur commented May 5, 2016

this PR provides a mechanism by which info, warning, and error messages can be organized hierarchically and selectively redirected to an IO stream of your choice. a keyword argument provided to warn identifies it's position within the hierarchy by subclassing a new abstract type Warning. a new command logging() is used to maintain a set of branches within the hierarchy mapping between the kind of message, and optionally specific modules and/or functions, to a stream.

for example, given this module:

module Foo
    function bar()
        info("barinfo")
        warn("barwarn")
        error("barerror")
    end
    function pooh()
        info("poohinfo")
        warn("poohwarn")
        error("pooherror")
    end
end

the user could selectively mute warn() in pooh() by

julia> Foo.bar()
INFO: barinfo (Foo.bar)
WARNING: barwarn (Foo.bar)
ERROR: barerror
 in bar() at ./REPL[1]:5
 in eval(::Module, ::Any) at ./boot.jl:230

julia> Foo.pooh()
INFO: poohinfo (Foo.pooh)
WARNING: poohwarn (Foo.pooh)
ERROR: pooherror
 in pooh() at ./REPL[1]:10
 in eval(::Module, ::Any) at ./boot.jl:230

julia> logging(DevNull, Foo, :pooh, kind=:warn)

julia> Foo.bar()
INFO: barinfo (Foo.bar)
WARNING: barwarn (Foo.bar)
ERROR: barerror
 in bar() at ./REPL[1]:5
 in eval(::Module, ::Any) at ./boot.jl:230

julia> Foo.pooh()
INFO: poohinfo (Foo.pooh)
ERROR: pooherror
 in pooh() at ./REPL[1]:10
 in eval(::Module, ::Any) at ./boot.jl:230

julia> 

@bjarthur bjarthur force-pushed the squelch branch 2 times, most recently from 08beb50 to ba7ad9d Compare May 6, 2016 13:42
@bjarthur
Copy link
Contributor Author

bjarthur commented May 6, 2016

version 2 is new and improved! can now just specify a particular module / function to mute; so Pkg code doesn't need to be modified to mute it.

@StefanKarpinski
Copy link
Member

I'm willing to have this for now, although I really think we need a more general system for logging info messages, warnings, and errors and controlling where that output goes. "Squelching" is just logging to /dev/null.

@tkelman
Copy link
Contributor

tkelman commented May 6, 2016

Kind of a strange name as well, is there precedent for this choice elsewhere?

@bjarthur
Copy link
Contributor Author

bjarthur commented May 7, 2016

version 3 is more general for info and warn as stefan requested. error forthcoming...

@bjarthur bjarthur force-pushed the squelch branch 2 times, most recently from f0461ef to 4cde358 Compare May 8, 2016 16:39
@bjarthur
Copy link
Contributor Author

bjarthur commented May 8, 2016

version 4 now redirects error too.

a few questions:

1. i'm having trouble typing some of the input args. see both redirect() and logging() in util.jl. can someone please explain why these don't work?

  1. sprint() with error() throws an error, but works fine with info() or warn(). because of this there are no tests for redirecting error() yet. is there any easy way to make it behave the same?

3. both REPL.jl and client.jl define display_error() nearly identically. can this be made more succinct by having one call the other?

thanks.

@timholy
Copy link
Member

timholy commented May 8, 2016

For your first question, not sure without more information about the error. If it happens when julia is being built, then it's likely that this type doesn't yet exist at the time that util.jl is included. See sysimg.jl for the sequence.

I'm tentatively assigning @StefanKarpinski to be in charge of helping bring this to conclusion.

@bjarthur
Copy link
Contributor Author

thanks @timholy . re-ordering sysimg.jl plus correcting a few stupid mistakes on my part fixed the typing problem.

now just need to figure out how to capture the output of error() in the tests...

Raise an `ErrorException` with the given message.

See also `logging`.
"""
Copy link
Contributor

Choose a reason for hiding this comment

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

the docstring should be attached to the signature that it corresponds to

@bjarthur bjarthur force-pushed the squelch branch 3 times, most recently from 2a17ff8 to 9f9396e Compare May 17, 2016 22:10
@bjarthur bjarthur changed the title RFC: add squelch() to mute warn() add logging() to redirect info/warn/error() May 17, 2016
@bjarthur
Copy link
Contributor Author

i think this is ready to merge.

i tried my best to add tests for error, but there is no way that i can see to do this. it all works when tested by hand though.

are there any comments / suggestions to the API or implementation?

@timholy
Copy link
Member

timholy commented Jun 16, 2016

@StefanKarpinski, are you willing to mentor this one through?

@StefanKarpinski
Copy link
Member

@JeffBezanson, what do you think about this?

@timholy timholy added the needs decision A decision on this change is needed label Sep 28, 2016
@tkelman
Copy link
Contributor

tkelman commented Dec 29, 2016

@Rory-Finnegan do you (or any of your colleagues interested in logging) have any opinions about this implementation or whether getting this version incorporated into base for 0.6 would be good/bad/neutral ? Thanks!

@ViralBShah
Copy link
Member

@tanmaykm also uses Logging.jl a fair bit, and may have a perspective here.

@rofinn
Copy link
Contributor

rofinn commented Dec 30, 2016

@tkelman I'm very interested in having nice flexible logging functionality in julia, but I'm mostly neutral regarding this implementation. I don't particularly like this solution and won't use this functionality myself, but I don't have any issue with providing this in base for now.

FWIW, I think Memento.jl will cover what I need/expect in terms of logging functionality for now. @noah79 you may find the table on this page useful.

@bjarthur
Copy link
Contributor Author

this PR permits users to redirect info/warn/error messages in a particular module or function irrespective of whether the call uses a particular logging package. this was my principle design feature, and it is not possible without directly modifying Base. being able to silence any message, including those originating from Base, is huge.

if isempty(log_to)
return io
else
if length(log_to)==1 && in((nothing,nothing),keys(log_to))
Copy link
Contributor

Choose a reason for hiding this comment

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

why isn't this haskey ?


Stream output of informational, warning, and/or error messages to `io`,
overriding what was otherwise specified. Optionally, divert stream only for
module `m`, or specifically function `f` within `m`. `kind` can also be
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of "also" I'd say "kind can be :all (the default), :info, :warn, or :error."

logging(kind=:error)
@test all(contains.(sprint(Logging.bar), ["INFO: barinfo", "WARNING: barwarn"]))
@test all(contains.(sprint(Logging.pooh), ["INFO: poohinfo", "WARNING: poohwarn"]))
@test all(contains.(sprint(foo), ["INFO: fooinfo", "WARNING: foowarn"]))
Copy link
Contributor

Choose a reason for hiding this comment

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

shouldn't these include error once again?

bt = bt[1:eval_ind-1]
end
with_output_color(Base.error_color(), io) do io
showerror(IOContext(io, :limit => true), er, bt)
Copy link
Contributor

Choose a reason for hiding this comment

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

The version of this from REPL.jl had been setting :limit => true, but the old implementation in client.jl wasn't - what outputs are you now limiting that weren't before?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

my assumption was that whoever added :limit => true to REPL.jl simply forgot to add it to client.jl as well. according to the IOContext docstring it simply truncates long containers with ellipses.

as an alternative, we could continue to keep maintaining two versions of display_error. i was just trying to remove some repetition figuring it'd make maintenance easier.

Copy link
Contributor

Choose a reason for hiding this comment

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

what used to determine which of the two would get called?

@@ -507,7 +507,7 @@ temp_pkg_dir() do

ret, out, err = @grab_outputs Pkg.update("Example")
@test ret === nothing && out == ""
@test contains(err, "INFO: Package Example: skipping update (pinned)...\n")
@test contains(err, "INFO: Package Example: skipping update (pinned)...")
Copy link
Contributor

Choose a reason for hiding this comment

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

do you know why these changed?

if eval_ind != 0
bt = bt[1:eval_ind-1]
end
with_output_color(Base.error_color(), io) do io
Copy link
Member

@KristofferC KristofferC Jan 1, 2017

Choose a reason for hiding this comment

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

This doesn't look right compared to whats in REPL.jl.

Copy link
Contributor Author

@bjarthur bjarthur Jan 1, 2017

Choose a reason for hiding this comment

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

yet this line remains precisely what used to be in client.jl; i've left this line unchanged.

IIRC a few months ago the two copies of display_error (in client.jl and REPL.jl) were identical. there has been more than one occasion where someone changed one, but not the other, when i think they should've changed both, and either forgot to or were unaware of the other copy.

please let me know if i am wrong in thinking that the two copies shouldn't behave identically. if this is not the case, then this PR should be modified so as not to delete the one in REPL.jl.

Copy link
Member

Choose a reason for hiding this comment

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

Previously the REPL.jl called Base.showerror. Now it calls this method which does not do the same thing as the one you deleted from REPL.jl. The result is that errors being printed to the REPL will now be different.

It looks like

display_error(e, catch_backtrace())
is the only location which calls the client.jl version of display_error. Updating the client.jl to match REPL.jl would be the best way forward in my opinion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's a do-block. showerror is called in the next line.

Copy link
Member

Choose a reason for hiding this comment

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

I know. That do block is not present in REPL.jl.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i'm confused. can you be more precise in describing what you want changed?

julia v0.5.0 had no calls to with_output_color until you added it to both REPL.jl and client.jl. at some later point, it was deleted from REPL.jl but not client.jl. are you saying i should delete your change from the latter as well?

it still seems to me that if the goal is that the output should be the same, then there should just be a single function, and not two.

Copy link
Member

Choose a reason for hiding this comment

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

julia v0.5.0 had no calls to with_output_color until you added it to both REPL.jl and client.jl

No, that commit simply changed, :red -> Base.error_color(). It did not add or remove any do blocks.

In a later PR the do block was deleted from REPL.jl. I am saying you can do the same in client.jl.

@bjarthur
Copy link
Contributor Author

bjarthur commented Jan 1, 2017

the one test that fails appears to be spurious. is there a way to just re-do it and not all the others?

@KristofferC
Copy link
Member

Restarted, failing worker. Previous failing log at https://gist.github.com/KristofferC/486937a09a0ef731ac2c2b2a3fb64ee4

@bjarthur
Copy link
Contributor Author

bjarthur commented Jan 2, 2017

thanks. how could i have done that myself?

@KristofferC
Copy link
Member

You'd need member status for the repo.

@tkelman
Copy link
Contributor

tkelman commented Jan 4, 2017

I'm inclined to merge this if no one objects in the next day or so.

@kmsquire
Copy link
Member

kmsquire commented Jan 5, 2017

As the author of Logging.jl, I'll chime in and say that silencing or redirecting info, warn, and/or error messages seems a reasonable thing to have, although it seems to me this is mostly to remove warts--e.g., to stop reams of deprecation warnings or other verbosity that should really be fixed, but for some reason the user is unable or unwilling to do so.

This implementation seems reasonable enough. It's a little verbose to silence or redirect things, and because of this, it probably won't be used much, but it's probably better to at least have this functionality.

I agree with the above statements that a fuller (extensible) logging module belongs in base, and that, except for lack of documentation, Memento.jl looks like an interesting implementation.

A couple of things I would love to see (but won't have any time to work on soon):

  1. An interface using Log as the module prefix (mimicking Pkg). Log is short enough that it's not annoying to write always, and it sequesters the functions within a module so they don't pollute the programmer's namespace. Logging macros could be implemented using something like the methods in Logging.jl.

  2. Stop error from throwing an exception (handled by 1.). It'll probably be annoying to deprecate this...)

@rofinn
Copy link
Contributor

rofinn commented Jan 5, 2017

@kmsquire I realize that this is a bit off topic, but if you have specific documentation concerns feel free to open an issue. I realize that the README is a bit sparse, but I'd appreciate your feedback on what's missing on the hosted docs.

@tkelman
Copy link
Contributor

tkelman commented Jan 5, 2017

Log would read a bit ambiguously with logarithm to me. An implementation big enough to be a package should probably stay that way, but if widely liked and used we could consider it for inclusion as a "default package." Modifications needed to base to better support that kind of logging module can be considered going forward, of course.

@tkelman tkelman merged commit 8d369bf into JuliaLang:master Jan 5, 2017
@kmsquire
Copy link
Member

kmsquire commented Jan 9, 2017

@bjarthur, congrats on your perseverance in getting this merged!

Off topic:

@Rory-Finnegan, sorry, I missed the link to the full docs at the top of the readme. Many packages Include an explicit pointer to the full docs (if they exist)--maybe you could add such a line? (I'd submit a PR, but I'm mostly off grid with no computer for the next week.)

@colinfang
Copy link
Contributor

@Rory-Finnegan I should have found your Memento.jl earlier so that I didn't have to write MiniLogging.jl for my company.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs decision A decision on this change is needed
Projects
None yet
Development

Successfully merging this pull request may close these issues.