diff --git a/src/Logging.jl b/src/Logging.jl index 8db72296..a60340ce 100644 --- a/src/Logging.jl +++ b/src/Logging.jl @@ -1,19 +1,22 @@ module Logging using Compat +using Compat.Libc: strftime import Base: show, info, warn export debug, info, warn, err, critical, log, @debug, @info, @warn, @err, @error, @critical, @log, Logger, - LogLevel, DEBUG, INFO, WARNING, ERROR, CRITICAL, OFF + LogLevel, DEBUG, INFO, WARNING, ERROR, CRITICAL, OFF, + LogFacility, + SysLog if VERSION < v"0.4.0-dev+3587" include("enum.jl") end -@enum LogLevel DEBUG INFO WARNING ERROR CRITICAL OFF +@enum LogLevel OFF=-1 CRITICAL=2 ERROR WARNING INFO=6 DEBUG try DEBUG < CRITICAL @@ -21,14 +24,60 @@ catch Base.isless(x::LogLevel, y::LogLevel) = isless(x.val, y.val) end +function Base.convert(::Type{LogLevel}, x::AbstractString) + Dict("OFF"=>OFF, + "CRITICAL"=>CRITICAL, + "ERROR"=>ERROR, + "WARNING"=>WARNING, + "INFO"=>INFO, + "DEBUG"=>DEBUG)[x] +end + +@enum LogFacility LOG_KERN LOG_USER LOG_MAIL LOG_DAEMON LOG_AUTH LOG_SYSLOG LOG_LPR LOG_NEWS LOG_UUCP LOG_CRON LOG_AUTHPRIV LOG_LOCAL0=16 LOG_LOCAL1 LOG_LOCAL2 LOG_LOCAL3 LOG_LOCAL4 LOG_LOCAL5 LOG_LOCAL6 LOG_LOCAL7 + +try + LOG_KERN < LOG_USER +catch + Base.isless(x::LogFacility, y::LogFacility) = isless(x.val, y.val) +end + +type SysLog + socket::UDPSocket + ip::IPv4 + port::UInt16 + facility::LogFacility + machine::AbstractString + user::AbstractString + maxlength::UInt16 + + SysLog(host::AbstractString, + port::Int, + facility::LogFacility=LOG_USER, + machine::AbstractString=gethostname(), + user::AbstractString=Base.source_path()==nothing ? "" : basename(Base.source_path()), + maxlength::Int=1024) = new( + UDPSocket(), + getaddrinfo(host), + (@compat UInt16(port)), + facility, + machine, + user, + (@compat UInt16(maxlength)) + ) +end + +LogOutput = @compat Union{IO,SysLog} + type Logger name::AbstractString level::LogLevel - output::IO + output::Array{LogOutput,1} parent::Logger - Logger(name::AbstractString, level::LogLevel, output::IO, parent::Logger) = new(name, level, output, parent) - Logger(name::AbstractString, level::LogLevel, output::IO) = (x = new(); x.name = name; x.level=level; x.output=output; x.parent=x) + Logger(name::AbstractString, level::LogLevel, output::IO, parent::Logger) = new(name, level, [output], parent) + Logger(name::AbstractString, level::LogLevel, output::IO) = (x = new(); x.name = name; x.level=level; x.output=[output]; x.parent=x) + Logger(name::AbstractString, level::LogLevel, output::Array{LogOutput,1}, parent::Logger) = new(name, level, output, parent) + Logger(name::AbstractString, level::LogLevel, output::Array{LogOutput,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([logger.name, @@ -37,9 +86,26 @@ show(io::IO, logger::Logger) = print(io, "Logger(", join([logger.name, 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) +write_log{T<:IO}(output::T, color::Symbol, msg::AbstractString) = (print(output, msg); flush(output)) +write_log(output::Base.TTY, color::Symbol, msg::AbstractString) = Base.print_with_color(color, output, msg) + +function log(syslog::SysLog, level::LogLevel, color::Symbol, logger_name::AbstractString, msg...) + # syslog needs a timestamp in the form: YYYY-MM-DDTHH:MM:SS-TZ:TZ + t = time() + timestamp = string(strftime("%Y-%m-%dT%H:%M:%S",t), strftime("%z",t)[1:end-2], ":", strftime("%z",t)[end-1:end]) + logstring = string("<", ((@compat UInt16(syslog.facility)) << 3) + (@compat UInt16(level)), ">1 ", timestamp, " ", syslog.machine, " ", syslog.user, " - - - ", level, ":", logger_name,":", msg...) + write_log(syslog, color, logstring) +end + +function log{T<:IO}(output::T, level::LogLevel, color::Symbol, logger_name::AbstractString, msg...) + logstring = string(strftime("%d-%b %H:%M:%S",time()),":",level, ":",logger_name,":", msg...,"\n") + write_log(output, color, logstring) +end + for (fn,lvl,clr) in ((:debug, DEBUG, :cyan), (:info, INFO, :blue), (:warn, WARNING, :magenta), @@ -47,13 +113,9 @@ for (fn,lvl,clr) in ((:debug, DEBUG, :cyan), (:critical, CRITICAL, :red)) @eval function $fn(logger::Logger, msg...) - if $lvl >= logger.level - logstring = string(Libc.strftime("%d-%b %H:%M:%S",time()),":",$lvl, ":",logger.name,":", msg...,"\n") - if isa(logger.output, Base.TTY) - Base.print_with_color($(Expr(:quote, clr)), logger.output, logstring ) - else - print(logger.output, logstring) - flush(logger.output) + if $lvl <= logger.level + for output in logger.output + log(output, $lvl, $(Expr(:quote, clr)), logger.name, msg...) end end end @@ -72,9 +134,11 @@ function configure(logger=_root; args...) end for (tag, val) in args - tag == :io ? (logger.output = val::IO) : - tag == :output ? (logger.output = val::IO) : - tag == :filename ? (logger.output = open(val, "a")) : + tag == :io ? typeof(val) <: AbstractArray ? (logger.output = val) : + (logger.output = [val::LogOutput]) : + tag == :output ? typeof(val) <: AbstractArray ? (logger.output = val) : + (logger.output = [val::LogOutput]) : + tag == :filename ? (logger.output = [open(val, "a")]) : tag == :level ? (logger.level = val::LogLevel) : tag == :override_info ? nothing : # handled below tag == :parent ? nothing : # handled above diff --git a/src/enum.jl b/src/enum.jl index 3b3bc638..f107ad7b 100644 --- a/src/enum.jl +++ b/src/enum.jl @@ -87,7 +87,7 @@ macro enum(T,syms...) if max(abs(lo),abs(hi)) < typemax(Uint32) enumT = Uint32 else - enumT = Uint64 + enumT = UInt64 end end blk = quote diff --git a/test/runtests.jl b/test/runtests.jl index 3c8e2652..44fb9a1d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,3 +1,4 @@ +using Logging: debug, info, warn include("log_test.jl") include("macro_test1.jl") include("macro_test2.jl")