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

RFC: Unexport logging functions #48

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ os:
- linux
- osx
julia:
- 0.4
- 0.5
- 0.6
- nightly
notifications:
email: false
Expand Down
68 changes: 16 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
[![Build Status](https://travis-ci.org/kmsquire/Logging.jl.svg?branch=master)](https://travis-ci.org/kmsquire/Logging.jl)
[![Logging](http://pkg.julialang.org/badges/Logging_0.4.svg)](http://pkg.julialang.org/?pkg=Logging)


Logging.jl: Basic logging for Julia
===================================

[![PkgEval: Julia v0.4](http://pkg.julialang.org/badges/Logging_0.4.svg)](http://pkg.julialang.org/?pkg=Logging)
[![PkgEval: Julia v0.5](http://pkg.julialang.org/badges/Logging_0.5.svg)](http://pkg.julialang.org/?pkg=Logging)
[![PkgEval: Julia v0.6](http://pkg.julialang.org/badges/Logging_0.6.svg)](http://pkg.julialang.org/?pkg=Logging)

[![TravisCI: Linux, OSX](https://travis-ci.org/kmsquire/Logging.jl.svg?branch=master)](https://travis-ci.org/kmsquire/Logging.jl)
[![AppVeyorCI: Windows](https://ci.appveyor.com/api/projects/status/7cj5kaj8gcxmltho?svg=true)](https://ci.appveyor.com/project/kmsquire/logging-jl)

This module provides basic logging facilities for Julia. It was inspired somewhat by logging in Python.

Install with `Pkg.add("Logging")` at the Julia prompt.
Expand All @@ -20,11 +23,11 @@ using Logging
# Logging.configure(level=WARNING)

function log_test()
debug("debug message")
info("info message")
warn("warning message")
err("error message")
critical("critical message")
Logging.debug("debug message")
Logging.info("info message")
Logging.warn("warning message")
Logging.error("error message")
Logging.critical("critical message")
end

println("Setting level=DEBUG")
Expand Down Expand Up @@ -123,7 +126,7 @@ function macro_log_test()
@debug("debug message")
@info("info message")
@warn("warning message")
@err("error message")
@error("error message")
@critical("critical message")
end

Expand All @@ -150,7 +153,7 @@ function macro_log_test()
@debug("debug message")
@info("info message")
@warn("warning message")
@err("error message")
@error("error message")
@critical("critical message")
end

Expand All @@ -176,7 +179,7 @@ function macro_log_test()
@debug("debug message")
@info("info message")
@warn("warning message")
@err("error message")
@error("error message")
@critical("critical message")
end

Expand Down Expand Up @@ -235,44 +238,5 @@ julia> mum_logger = Logger("Mum");
julia> Logging.configure(mum_logger, level=INFO);
julia> son_logger = Logger("Son", parent=mum_logger);
julia> son_logger.level
INFO
```

Notes
-----
* By default, `Logging.info` masks `Base.info`. However, if `Base.info` is called before
`using Logging`, `info` will always refer to the `Base` version.

```julia
julia> info("Here's some info.")
INFO: Here's some info.

julia> using Logging
Warning: using Logging.info in module Main conflicts with an existing identifier.

julia> @Logging.configure(level=Logging.INFO)
Logger(root,INFO,TTY(open, 0 bytes waiting),root)

julia> info("Still using Base.info")
INFO: Still using Base.info

julia> Logging.info("You can still fully qualify Logging.info.")
17-Jan 13:19:56:INFO:root:You can still fully qualify Logging.info.
```

If this is not desirable, you may call `@Logging.configure` with `override_info=true`:

```julia
julia> info("Here's some info again.")
INFO: Here's some info again.

julia> using Logging
Warning: using Logging.info in module Main conflicts with an existing identifier.

julia> @Logging.configure(level=Logging.INFO, override_info=true)
Warning: Method definition info(AbstractString...,) in module Base at util.jl:216 overwritten in module Main at /Users/kevin/.julia/v0.4/Logging/src/Logging.jl:85.
Logger(root,INFO,TTY(open, 0 bytes waiting),root)

julia> info("Now we're using Logging.info")
17-Jan 13:17:20:INFO:root:Now we're using Logging.info
INFO::Logging.LogLevel = 6
```
2 changes: 1 addition & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1 +1 @@
julia 0.4
julia 0.5
11 changes: 6 additions & 5 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
environment:
matrix:
- JULIAVERSION: "julialang/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
- JULIAVERSION: "julialang/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
- JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe"
- JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe"
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe"
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe"

branches:
only:
Expand All @@ -17,9 +17,10 @@ notifications:
on_build_status_changed: false

install:
- ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12"
# Download most recent Julia Windows binary
- ps: (new-object net.webclient).DownloadFile(
$("http://s3.amazonaws.com/"+$env:JULIAVERSION),
$env:JULIA_URL,
"C:\projects\julia-binary.exe")
# Run installer silently, output to C:\projects\julia
- C:\projects\julia-binary.exe /S /D=C:\projects\julia
Expand Down
132 changes: 111 additions & 21 deletions src/Logging.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ __precompile__()

module Logging

import Base: show, info, warn

export debug, info, warn, err, critical,
@debug, @info, @warn, @err, @error, @critical, @log,
Logger,
export Logger,
LogLevel, DEBUG, INFO, WARNING, ERROR, CRITICAL, OFF,
LogFacility,
SysLog
Expand Down Expand Up @@ -72,13 +68,13 @@ type Logger
Logger{T<:LogOutput}(name::AbstractString, level::LogLevel, output::Array{T,1}) = (x = new(); x.name = name; x.level=level; x.output=output; x.parent=x)
end

show(io::IO, logger::Logger) = print(io, "Logger(", join(Any[logger.name,
logger.level,
logger.output,
logger.parent.name], ","), ")")
Base.show(io::IO, logger::Logger) = print(io, "Logger(", join(Any[logger.name,
logger.level,
logger.output,
logger.parent.name], ","), ")")

const _root = Logger("root", WARNING, STDERR)
Logger(name::AbstractString;args...) = configure(Logger(name, WARNING, STDERR, _root); args...)
Logger(name::AbstractString;args...) = _configure(Logger(name, WARNING, STDERR, _root); args...)
Logger() = Logger("logger")

write_log(syslog::SysLog, color::Symbol, msg::AbstractString) = send(syslog.socket, syslog.ip, syslog.port, length(msg) > syslog.maxlength ? msg[1:syslog.maxlength] : msg)
Expand All @@ -101,7 +97,7 @@ end
for (fn,lvl,clr) in ((:debug, DEBUG, :cyan),
(:info, INFO, :blue),
(:warn, WARNING, :magenta),
(:err, ERROR, :red),
(:error, ERROR, :red),
(:critical, CRITICAL, :red))

@eval function $fn(logger::Logger, msg...)
Expand All @@ -113,10 +109,11 @@ for (fn,lvl,clr) in ((:debug, DEBUG, :cyan),
end

@eval $fn(msg...) = $fn(_root, msg...)

end

function configure(logger=_root; args...)
@deprecate err Logging.error

function _configure(logger=_root; args...)
for (tag, val) in args
if tag == :parent
logger.parent = parent = val::Logger
Expand All @@ -134,26 +131,119 @@ function configure(logger=_root; args...)
tag == :level ? (logger.level = val::LogLevel) :
tag == :override_info ? nothing : # handled below
tag == :parent ? nothing : # handled above
(Base.error("Logging: unknown configure argument \"$tag\""))
(throw(ArgumentError("Logging: unknown configure argument \"$tag\"")))
end

logger
end

override_info(;args...) = (:override_info, true) in args
"""
Module which contains versions of the logging functions which
print a deprecation warning when used.

This is useful for transitioning users to either call the qualified
function names (e.g., `Logging.info`, `Logging.warn`, etc.) or explicitly
import the functions.
"""
module _Deprecated

import Logging

deprecation_printed = false

for fn in (:debug, :info, :warn, :error, :critical)
@eval function $fn(args...)
global deprecation_printed
if !deprecation_printed
Base.warn("""
In the future, `using Logging` will not import the following
logging functions:

debug
info
warn
error
critical

You can either use these functions by qualifying them
(e.g., `Logging.debug(...)`, `Logging.warn(...)`, etc.),
or by explicitly importing them:

using Logging
import Logging: debug, info, warn, error, critical

""")
deprecation_printed = true
end

Logging.$fn(args...)
end
end

end # module _Deprecated

function _imported_with_using()
return all(isdefined.(names(Logging)))
end

function _logging_funcs_imported()
# isdefined "reifies" any object that is defined, making it impossible
# it to override it via import. Therefore, we don't check
# `info`, `warn`, and `error`, since these functions exist in
# Base and won't be overridden if we check them here.
return all(isdefined.([:debug, #=:info, :warn, :error,=# :critical]))
end

function _macro_loaded(macroname)
expr = macroexpand(:($(macroname)("hi")))
return expr.head != :error
end

function _macros_loaded()
return all(_macro_loaded.([:@debug, :@info, :@warn, :@error, :@critical]))
end

_src_dir = dirname(@__FILE__)

# Keyword arguments x=1 passed to macros are parsed as Expr(:(=), :x, 1) but
# must be passed as Expr(:(kw), :x, 1) in Julia v0.6.
if VERSION < v"0.6-"
fix_kwarg(x) = x
else
fix_kwarg(x::Symbol) = x
fix_kwarg(e::Expr) = e.head == :(=) ? Expr(:(kw), e.args...) : e
end

macro configure(args...)
_args = gensym()
quote
logger = Logging.configure($(args...))
if Logging.override_info($(args...))
function Base.info(msg::AbstractString...)
Logging.info(Logging._root, msg...)
logger = Logging._configure($([fix_kwarg(a) for a in args]...))

if Logging._imported_with_using() && !Logging._logging_funcs_imported()
# We assume that the user has not manually
# imported the Logging functions, and we import
# versions of these which print a deprecation warning
try
import Logging._Deprecated: info, warn, debug, error, critical
catch
Base.warn("Please call Logging.@configure from the top level (module) scope.")
end
end
include(joinpath(Pkg.dir("Logging"), "src", "logging_macros.jl"))

if !Logging._macros_loaded()
include(joinpath(Logging._src_dir, "logging_macros.jl"))
end
logger
end
end

function configure(args...; kwargs...)
throw(ErrorException("""
The functional form of Logging.configure(...) is no longer supported.
Instead, call

Logging.@configure(...)

"""))
end

end # module
6 changes: 3 additions & 3 deletions src/logging_macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ for (mac,fn,lvl) in ((:debug, :(Logging.debug), Logging.DEBUG),
(:info, :(Logging.info), Logging.INFO),
(:warn, :(Logging.warn), Logging.WARNING),
(:err, :(Logging.err), Logging.ERROR),
(:error, :(Logging.err), Logging.ERROR),
(:error, :(Logging.error), Logging.ERROR),
(:critical, :(Logging.critical), Logging.CRITICAL))

@eval macro $mac(msg...)
Expand All @@ -15,9 +15,9 @@ for (mac,fn,lvl) in ((:debug, :(Logging.debug), Logging.DEBUG),
end

if $lvl > level
:nothing
esc(:nothing)
else
Expr(:call, $fn, msg...)
Expr(:call, $fn, [esc(m) for m in msg]...)
end
end

Expand Down
Loading