pax_global_header00006660000000000000000000000064127171305420014514gustar00rootroot0000000000000052 comment=9545b249b3aacafa97f79e0838b02b274adc6f5f tendermint-log15-2.3-67-g9545b24/000077500000000000000000000000001271713054200157735ustar00rootroot00000000000000tendermint-log15-2.3-67-g9545b24/.travis.yml000066400000000000000000000001121271713054200200760ustar00rootroot00000000000000language: go go: - 1.1 - 1.2 - 1.3 - 1.4 - 1.5 - 1.6 - tip tendermint-log15-2.3-67-g9545b24/CONTRIBUTORS000066400000000000000000000002551271713054200176550ustar00rootroot00000000000000Contributors to log15: - Aaron L - Alan Shreve - Chris Hines - Ciaran Downey - Dmitry Chestnykh - Evan Shaw - Péter Szilágyi - Trevor Gattis - Vincent Vanackere tendermint-log15-2.3-67-g9545b24/LICENSE000066400000000000000000000010601271713054200167750ustar00rootroot00000000000000Copyright 2014 Alan Shreve Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. APACHE! tendermint-log15-2.3-67-g9545b24/README.md000066400000000000000000000057031271713054200172570ustar00rootroot00000000000000![obligatory xkcd](http://imgs.xkcd.com/comics/standards.png) # log15 [![godoc reference](https://godoc.org/github.com/inconshreveable/log15?status.png)](https://godoc.org/github.com/inconshreveable/log15) [![Build Status](https://travis-ci.org/inconshreveable/log15.svg?branch=master)](https://travis-ci.org/inconshreveable/log15) Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](http://golang.org/pkg/io/) and [`net/http`](http://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](http://golang.org/pkg/log/) package. ## Features - A simple, easy-to-understand API - Promotes structured logging by encouraging use of key/value pairs - Child loggers which inherit and add their own private context - Lazy evaluation of expensive operations - Simple Handler interface allowing for construction of flexible, custom logging configurations with a tiny API. - Color terminal support - Built-in support for logging to files, streams, syslog, and the network - Support for forking records to multiple handlers, buffering records for output, failing over from failed handler writes, + more ## Versioning The API of the master branch of log15 should always be considered unstable. If you want to rely on a stable API, you must vendor the library. ## Importing ```go import log "github.com/inconshreveable/log15" ``` ## Examples ```go // all loggers can have key/value context srvlog := log.New("module", "app/server") // all log messages can have key/value context srvlog.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate) // child loggers with inherited context connlog := srvlog.New("raddr", c.RemoteAddr()) connlog.Info("connection open") // lazy evaluation connlog.Debug("ping remote", "latency", log.Lazy{pingRemote}) // flexible configuration srvlog.SetHandler(log.MultiHandler( log.StreamHandler(os.Stderr, log.LogfmtFormat()), log.LvlFilterHandler( log.LvlError, log.Must.FileHandler("errors.json", log.JsonFormat()))) ``` ## Breaking API Changes The following commits broke API stability. This reference is intended to help you understand the consequences of updating to a newer version of log15. - 57a084d014d4150152b19e4e531399a7145d1540 - Added a `Get()` method to the `Logger` interface to retrieve the current handler - 93404652ee366648fa622b64d1e2b67d75a3094a - `Record` field `Call` changed to `stack.Call` with switch to `github.com/go-stack/stack` - a5e7613673c73281f58e15a87d2cf0cf111e8152 - Restored `syslog.Priority` argument to the `SyslogXxx` handler constructors ## FAQ ### The varargs style is brittle and error prone! Can I have type safety please? Yes. Use `log.Ctx`: ```go srvlog := log.New(log.Ctx{"module": "app/server"}) srvlog.Warn("abnormal conn rate", log.Ctx{"rate": curRate, "low": lowRate, "high": highRate}) ``` ## License Apache tendermint-log15-2.3-67-g9545b24/bench_test.go000066400000000000000000000042471271713054200204470ustar00rootroot00000000000000package log15 import ( "bytes" "testing" "time" ) func BenchmarkStreamNoCtx(b *testing.B) { lg := New() buf := bytes.Buffer{} lg.SetHandler(StreamHandler(&buf, LogfmtFormat())) for i := 0; i < b.N; i++ { lg.Info("test message") buf.Reset() } } func BenchmarkDiscard(b *testing.B) { lg := New() lg.SetHandler(DiscardHandler()) for i := 0; i < b.N; i++ { lg.Info("test message") } } func BenchmarkCallerFileHandler(b *testing.B) { lg := New() lg.SetHandler(CallerFileHandler(DiscardHandler())) for i := 0; i < b.N; i++ { lg.Info("test message") } } func BenchmarkCallerFuncHandler(b *testing.B) { lg := New() lg.SetHandler(CallerFuncHandler(DiscardHandler())) for i := 0; i < b.N; i++ { lg.Info("test message") } } func BenchmarkLogfmtNoCtx(b *testing.B) { r := Record{ Time: time.Now(), Lvl: LvlInfo, Msg: "test message", Ctx: []interface{}{}, } logfmt := LogfmtFormat() for i := 0; i < b.N; i++ { logfmt.Format(&r) } } func BenchmarkJsonNoCtx(b *testing.B) { r := Record{ Time: time.Now(), Lvl: LvlInfo, Msg: "test message", Ctx: []interface{}{}, } jsonfmt := JsonFormat() for i := 0; i < b.N; i++ { jsonfmt.Format(&r) } } func BenchmarkMultiLevelFilter(b *testing.B) { handler := MultiHandler( LvlFilterHandler(LvlDebug, DiscardHandler()), LvlFilterHandler(LvlError, DiscardHandler()), ) lg := New() lg.SetHandler(handler) for i := 0; i < b.N; i++ { lg.Info("test message") } } func BenchmarkDescendant1(b *testing.B) { lg := New() lg.SetHandler(DiscardHandler()) lg = lg.New() for i := 0; i < b.N; i++ { lg.Info("test message") } } func BenchmarkDescendant2(b *testing.B) { lg := New() lg.SetHandler(DiscardHandler()) for i := 0; i < 2; i++ { lg = lg.New() } for i := 0; i < b.N; i++ { lg.Info("test message") } } func BenchmarkDescendant4(b *testing.B) { lg := New() lg.SetHandler(DiscardHandler()) for i := 0; i < 4; i++ { lg = lg.New() } for i := 0; i < b.N; i++ { lg.Info("test message") } } func BenchmarkDescendant8(b *testing.B) { lg := New() lg.SetHandler(DiscardHandler()) for i := 0; i < 8; i++ { lg = lg.New() } for i := 0; i < b.N; i++ { lg.Info("test message") } } tendermint-log15-2.3-67-g9545b24/doc.go000066400000000000000000000277411271713054200171020ustar00rootroot00000000000000/* Package log15 provides an opinionated, simple toolkit for best-practice logging that is both human and machine readable. It is modeled after the standard library's io and net/http packages. This package enforces you to only log key/value pairs. Keys must be strings. Values may be any type that you like. The default output format is logfmt, but you may also choose to use JSON instead if that suits you. Here's how you log: log.Info("page accessed", "path", r.URL.Path, "user_id", user.id) This will output a line that looks like: lvl=info t=2014-05-02T16:07:23-0700 msg="page accessed" path=/org/71/profile user_id=9 Getting Started To get started, you'll want to import the library: import log "github.com/inconshreveable/log15" Now you're ready to start logging: func main() { log.Info("Program starting", "args", os.Args()) } Convention Because recording a human-meaningful message is common and good practice, the first argument to every logging method is the value to the *implicit* key 'msg'. Additionally, the level you choose for a message will be automatically added with the key 'lvl', and so will the current timestamp with key 't'. You may supply any additional context as a set of key/value pairs to the logging function. log15 allows you to favor terseness, ordering, and speed over safety. This is a reasonable tradeoff for logging functions. You don't need to explicitly state keys/values, log15 understands that they alternate in the variadic argument list: log.Warn("size out of bounds", "low", lowBound, "high", highBound, "val", val) If you really do favor your type-safety, you may choose to pass a log.Ctx instead: log.Warn("size out of bounds", log.Ctx{"low": lowBound, "high": highBound, "val": val}) Context loggers Frequently, you want to add context to a logger so that you can track actions associated with it. An http request is a good example. You can easily create new loggers that have context that is automatically included with each log line: requestlogger := log.New("path", r.URL.Path) // later requestlogger.Debug("db txn commit", "duration", txnTimer.Finish()) This will output a log line that includes the path context that is attached to the logger: lvl=dbug t=2014-05-02T16:07:23-0700 path=/repo/12/add_hook msg="db txn commit" duration=0.12 Handlers The Handler interface defines where log lines are printed to and how they are formated. Handler is a single interface that is inspired by net/http's handler interface: type Handler interface { Log(r *Record) error } Handlers can filter records, format them, or dispatch to multiple other Handlers. This package implements a number of Handlers for common logging patterns that are easily composed to create flexible, custom logging structures. Here's an example handler that prints logfmt output to Stdout: handler := log.StreamHandler(os.Stdout, log.LogfmtFormat()) Here's an example handler that defers to two other handlers. One handler only prints records from the rpc package in logfmt to standard out. The other prints records at Error level or above in JSON formatted output to the file /var/log/service.json handler := log.MultiHandler( log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JsonFormat())), log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler()) ) Logging File Names and Line Numbers This package implements three Handlers that add debugging information to the context, CallerFileHandler, CallerFuncHandler and CallerStackHandler. Here's an example that adds the source file and line number of each logging call to the context. h := log.CallerFileHandler(log.StdoutHandler()) log.Root().SetHandler(h) ... log.Error("open file", "err", err) This will output a line that looks like: lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" caller=data.go:42 Here's an example that logs the call stack rather than just the call site. h := log.CallerStackHandler("%+v", log.StdoutHandler()) log.Root().SetHandler(h) ... log.Error("open file", "err", err) This will output a line that looks like: lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" stack="[pkg/data.go:42 pkg/cmd/main.go]" The "%+v" format instructs the handler to include the path of the source file relative to the compile time GOPATH. The github.com/go-stack/stack package documents the full list of formatting verbs and modifiers available. Custom Handlers The Handler interface is so simple that it's also trivial to write your own. Let's create an example handler which tries to write to one handler, but if that fails it falls back to writing to another handler and includes the error that it encountered when trying to write to the primary. This might be useful when trying to log over a network socket, but if that fails you want to log those records to a file on disk. type BackupHandler struct { Primary Handler Secondary Handler } func (h *BackupHandler) Log (r *Record) error { err := h.Primary.Log(r) if err != nil { r.Ctx = append(ctx, "primary_err", err) return h.Secondary.Log(r) } return nil } This pattern is so useful that a generic version that handles an arbitrary number of Handlers is included as part of this library called FailoverHandler. Logging Expensive Operations Sometimes, you want to log values that are extremely expensive to compute, but you don't want to pay the price of computing them if you haven't turned up your logging level to a high level of detail. This package provides a simple type to annotate a logging operation that you want to be evaluated lazily, just when it is about to be logged, so that it would not be evaluated if an upstream Handler filters it out. Just wrap any function which takes no arguments with the log.Lazy type. For example: func factorRSAKey() (factors []int) { // return the factors of a very large number } log.Debug("factors", log.Lazy{factorRSAKey}) If this message is not logged for any reason (like logging at the Error level), then factorRSAKey is never evaluated. Dynamic context values The same log.Lazy mechanism can be used to attach context to a logger which you want to be evaluated when the message is logged, but not when the logger is created. For example, let's imagine a game where you have Player objects: type Player struct { name string alive bool log.Logger } You always want to log a player's name and whether they're alive or dead, so when you create the player object, you might do: p := &Player{name: name, alive: true} p.Logger = log.New("name", p.name, "alive", p.alive) Only now, even after a player has died, the logger will still report they are alive because the logging context is evaluated when the logger was created. By using the Lazy wrapper, we can defer the evaluation of whether the player is alive or not to each log message, so that the log records will reflect the player's current state no matter when the log message is written: p := &Player{name: name, alive: true} isAlive := func() bool { return p.alive } player.Logger = log.New("name", p.name, "alive", log.Lazy{isAlive}) Terminal Format If log15 detects that stdout is a terminal, it will configure the default handler for it (which is log.StdoutHandler) to use TerminalFormat. This format logs records nicely for your terminal, including color-coded output based on log level. Error Handling Becasuse log15 allows you to step around the type system, there are a few ways you can specify invalid arguments to the logging functions. You could, for example, wrap something that is not a zero-argument function with log.Lazy or pass a context key that is not a string. Since logging libraries are typically the mechanism by which errors are reported, it would be onerous for the logging functions to return errors. Instead, log15 handles errors by making these guarantees to you: - Any log record containing an error will still be printed with the error explained to you as part of the log record. - Any log record containing an error will include the context key LOG15_ERROR, enabling you to easily (and if you like, automatically) detect if any of your logging calls are passing bad values. Understanding this, you might wonder why the Handler interface can return an error value in its Log method. Handlers are encouraged to return errors only if they fail to write their log records out to an external source like if the syslog daemon is not responding. This allows the construction of useful handlers which cope with those failures like the FailoverHandler. Library Use log15 is intended to be useful for library authors as a way to provide configurable logging to users of their library. Best practice for use in a library is to always disable all output for your logger by default and to provide a public Logger instance that consumers of your library can configure. Like so: package yourlib import "github.com/inconshreveable/log15" var Log = log.New() func init() { Log.SetHandler(log.DiscardHandler()) } Users of your library may then enable it if they like: import "github.com/inconshreveable/log15" import "example.com/yourlib" func main() { handler := // custom handler setup yourlib.Log.SetHandler(handler) } Best practices attaching logger context The ability to attach context to a logger is a powerful one. Where should you do it and why? I favor embedding a Logger directly into any persistent object in my application and adding unique, tracing context keys to it. For instance, imagine I am writing a web browser: type Tab struct { url string render *RenderingContext // ... Logger } func NewTab(url string) *Tab { return &Tab { // ... url: url, Logger: log.New("url", url), } } When a new tab is created, I assign a logger to it with the url of the tab as context so it can easily be traced through the logs. Now, whenever we perform any operation with the tab, we'll log with its embedded logger and it will include the tab title automatically: tab.Debug("moved position", "idx", tab.idx) There's only one problem. What if the tab url changes? We could use log.Lazy to make sure the current url is always written, but that would mean that we couldn't trace a tab's full lifetime through our logs after the user navigate to a new URL. Instead, think about what values to attach to your loggers the same way you think about what to use as a key in a SQL database schema. If it's possible to use a natural key that is unique for the lifetime of the object, do so. But otherwise, log15's ext package has a handy RandId function to let you generate what you might call "surrogate keys" They're just random hex identifiers to use for tracing. Back to our Tab example, we would prefer to set up our Logger like so: import logext "github.com/inconshreveable/log15/ext" t := &Tab { // ... url: url, } t.Logger = log.New("id", logext.RandId(8), "url", log.Lazy{t.getUrl}) return t Now we'll have a unique traceable identifier even across loading new urls, but we'll still be able to see the tab's current url in the log messages. Must For all Handler functions which can return an error, there is a version of that function which will return no error but panics on failure. They are all available on the Must object. For example: log.Must.FileHandler("/path", log.JsonFormat) log.Must.NetHandler("tcp", ":1234", log.JsonFormat) Inspiration and Credit All of the following excellent projects inspired the design of this library: code.google.com/p/log4go github.com/op/go-logging github.com/technoweenie/grohl github.com/Sirupsen/logrus github.com/kr/logfmt github.com/spacemonkeygo/spacelog golang's stdlib, notably io and net/http The Name https://xkcd.com/927/ */ package log15 tendermint-log15-2.3-67-g9545b24/ext/000077500000000000000000000000001271713054200165735ustar00rootroot00000000000000tendermint-log15-2.3-67-g9545b24/ext/ext_test.go000066400000000000000000000043331271713054200207640ustar00rootroot00000000000000package ext import ( "errors" log "github.com/inconshreveable/log15" "math" "testing" ) func testHandler() (log.Handler, *log.Record) { rec := new(log.Record) return log.FuncHandler(func(r *log.Record) error { *rec = *r return nil }), rec } func TestHotSwapHandler(t *testing.T) { t.Parallel() h1, r1 := testHandler() l := log.New() h := HotSwapHandler(h1) l.SetHandler(h) l.Info("to h1") if r1.Msg != "to h1" { t.Fatalf("didn't get expected message to h1") } h2, r2 := testHandler() h.Swap(h2) l.Info("to h2") if r2.Msg != "to h2" { t.Fatalf("didn't get expected message to h2") } } func TestSpeculativeHandler(t *testing.T) { t.Parallel() // test with an even multiple of the buffer size, less than full buffer size // and not a multiple of the buffer size for _, count := range []int{10000, 50, 432} { recs := make(chan *log.Record) done := make(chan int) spec := SpeculativeHandler(100, log.ChannelHandler(recs)) go func() { defer close(done) expectedCount := int(math.Min(float64(count), float64(100))) expectedIdx := count - expectedCount for r := range recs { if r.Ctx[1] != expectedIdx { t.Errorf("Bad ctx 'i', got %d expected %d", r.Ctx[1], expectedIdx) return } expectedIdx++ expectedCount-- if expectedCount == 0 { // got everything we expected break } } select { case <-recs: t.Errorf("got an extra record we shouldn't have!") default: } }() lg := log.New() lg.SetHandler(spec) for i := 0; i < count; i++ { lg.Debug("test speculative", "i", i) } go spec.Flush() // wait for the go routine to finish <-done } } func TestErrorHandler(t *testing.T) { t.Parallel() h, r := testHandler() lg := log.New() lg.SetHandler(EscalateErrHandler( log.LvlFilterHandler(log.LvlError, h))) lg.Debug("some function result", "err", nil) if r.Msg != "" { t.Fatalf("Expected debug level message to be filtered") } lg.Debug("some function result", "err", errors.New("failed operation")) if r.Msg != "some function result" { t.Fatalf("Expected debug level message to be escalated and pass lvlfilter") } if r.Lvl != log.LvlError { t.Fatalf("Expected debug level message to be escalated to LvlError") } } tendermint-log15-2.3-67-g9545b24/ext/handler.go000066400000000000000000000064731271713054200205510ustar00rootroot00000000000000package ext import ( "os" "sync" "sync/atomic" "unsafe" log "github.com/inconshreveable/log15" ) // EscalateErrHandler wraps another handler and passes all records through // unchanged except if the logged context contains a non-nil error // value in its context. In that case, the record's level is raised // to LvlError unless it was already more serious (LvlCrit). // // This allows you to log the result of all functions for debugging // and still capture error conditions when in production with a single // log line. As an example, the following the log record will be written // out only if there was an error writing a value to redis: // // logger := logext.EscalateErrHandler( // log.LvlFilterHandler(log.LvlInfo, log.StdoutHandler)) // // reply, err := redisConn.Do("SET", "foo", "bar") // logger.Debug("Wrote value to redis", "reply", reply, "err", err) // if err != nil { // return err // } // func EscalateErrHandler(h log.Handler) log.Handler { return log.FuncHandler(func(r *log.Record) error { if r.Lvl > log.LvlError { for i := 1; i < len(r.Ctx); i++ { if v, ok := r.Ctx[i].(error); ok && v != nil { r.Lvl = log.LvlError break } } } return h.Log(r) }) } // SpeculativeHandler is a handler for speculative logging. It // keeps a ring buffer of the given size full of the last events // logged into it. When Flush is called, all buffered log records // are written to the wrapped handler. This is extremely for // continuosly capturing debug level output, but only flushing those // log records if an exceptional condition is encountered. func SpeculativeHandler(size int, h log.Handler) *Speculative { return &Speculative{ handler: h, recs: make([]*log.Record, size), } } type Speculative struct { mu sync.Mutex idx int recs []*log.Record handler log.Handler full bool } func (h *Speculative) Log(r *log.Record) error { h.mu.Lock() defer h.mu.Unlock() h.recs[h.idx] = r h.idx = (h.idx + 1) % len(h.recs) h.full = h.full || h.idx == 0 return nil } func (h *Speculative) Flush() { recs := make([]*log.Record, 0) func() { h.mu.Lock() defer h.mu.Unlock() if h.full { recs = append(recs, h.recs[h.idx:]...) } recs = append(recs, h.recs[:h.idx]...) // reset state h.full = false h.idx = 0 }() // don't hold the lock while we flush to the wrapped handler for _, r := range recs { h.handler.Log(r) } } // HotSwapHandler wraps another handler that may swapped out // dynamically at runtime in a thread-safe fashion. // HotSwapHandler is the same functionality // used to implement the SetHandler method for the default // implementation of Logger. func HotSwapHandler(h log.Handler) *HotSwap { hs := new(HotSwap) hs.Swap(h) return hs } type HotSwap struct { handler unsafe.Pointer } func (h *HotSwap) Log(r *log.Record) error { return (*(*log.Handler)(atomic.LoadPointer(&h.handler))).Log(r) } func (h *HotSwap) Swap(newHandler log.Handler) { atomic.StorePointer(&h.handler, unsafe.Pointer(&newHandler)) } // FatalHandler makes critical errors exit the program // immediately, much like the log.Fatal* methods from the // standard log package func FatalHandler(h log.Handler) log.Handler { return log.FuncHandler(func(r *log.Record) error { err := h.Log(r) if r.Lvl == log.LvlCrit { os.Exit(1) } return err }) } tendermint-log15-2.3-67-g9545b24/ext/id.go000066400000000000000000000017651271713054200175270ustar00rootroot00000000000000package ext import ( "fmt" "math/rand" "sync" "time" ) var r = rand.New(&lockedSource{src: rand.NewSource(time.Now().Unix())}) // RandId creates a random identifier of the requested length. // Useful for assigning mostly-unique identifiers for logging // and identification that are unlikely to collide because of // short lifespan or low set cardinality func RandId(idlen int) string { b := make([]byte, idlen) var randVal uint32 for i := 0; i < idlen; i++ { byteIdx := i % 4 if byteIdx == 0 { randVal = r.Uint32() } b[i] = byte((randVal >> (8 * uint(byteIdx))) & 0xFF) } return fmt.Sprintf("%x", b) } // lockedSource is a wrapper to allow a rand.Source to be used // concurrently (same type as the one used internally in math/rand). type lockedSource struct { lk sync.Mutex src rand.Source } func (r *lockedSource) Int63() (n int64) { r.lk.Lock() n = r.src.Int63() r.lk.Unlock() return } func (r *lockedSource) Seed(seed int64) { r.lk.Lock() r.src.Seed(seed) r.lk.Unlock() } tendermint-log15-2.3-67-g9545b24/format.go000066400000000000000000000134071271713054200176170ustar00rootroot00000000000000package log15 import ( "bytes" "encoding/json" "fmt" "reflect" "strconv" "strings" "time" ) const ( timeFormat = "2006-01-02T15:04:05-0700" termTimeFormat = "01-02|15:04:05" floatFormat = 'f' termMsgJust = 40 ) type Format interface { Format(r *Record) []byte } // FormatFunc returns a new Format object which uses // the given function to perform record formatting. func FormatFunc(f func(*Record) []byte) Format { return formatFunc(f) } type formatFunc func(*Record) []byte func (f formatFunc) Format(r *Record) []byte { return f(r) } // TerminalFormat formats log records optimized for human readability on // a terminal with color-coded level output and terser human friendly timestamp. // This format should only be used for interactive programs or while developing. // // [TIME] [LEVEL] MESAGE key=value key=value ... // // Example: // // [May 16 20:58:45] [DBUG] remove route ns=haproxy addr=127.0.0.1:50002 // func TerminalFormat() Format { return FormatFunc(func(r *Record) []byte { var color = 0 switch r.Lvl { case LvlCrit: color = 35 case LvlError: color = 31 case LvlWarn: color = 33 case LvlNotice: color = 32 case LvlInfo: color = 34 case LvlDebug: color = 36 } b := &bytes.Buffer{} lvl := strings.ToUpper(r.Lvl.String()) if color > 0 { fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg) } else { fmt.Fprintf(b, "[%s] [%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg) } // try to justify the log output for short messages if len(r.Ctx) > 0 && len(r.Msg) < termMsgJust { b.Write(bytes.Repeat([]byte{' '}, termMsgJust-len(r.Msg))) } // print the keys logfmt style logfmt(b, r.Ctx, color) return b.Bytes() }) } // LogfmtFormat prints records in logfmt format, an easy machine-parseable but human-readable // format for key/value pairs. // // For more details see: http://godoc.org/github.com/kr/logfmt // func LogfmtFormat() Format { return FormatFunc(func(r *Record) []byte { common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg} buf := &bytes.Buffer{} logfmt(buf, append(common, r.Ctx...), 0) return buf.Bytes() }) } func logfmt(buf *bytes.Buffer, ctx []interface{}, color int) { for i := 0; i < len(ctx); i += 2 { if i != 0 { buf.WriteByte(' ') } k, ok := ctx[i].(string) v := formatLogfmtValue(ctx[i+1]) if !ok { k, v = errorKey, formatLogfmtValue(k) } // XXX: we should probably check that all of your key bytes aren't invalid if color > 0 { fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=%s", color, k, v) } else { fmt.Fprintf(buf, "%s=%s", k, v) } } buf.WriteByte('\n') } // JsonFormat formats log records as JSON objects separated by newlines. // It is the equivalent of JsonFormatEx(false, true). func JsonFormat() Format { return JsonFormatEx(false, true) } // JsonFormatEx formats log records as JSON objects. If pretty is true, // records will be pretty-printed. If lineSeparated is true, records // will be logged with a new line between each record. func JsonFormatEx(pretty, lineSeparated bool) Format { jsonMarshal := json.Marshal if pretty { jsonMarshal = func(v interface{}) ([]byte, error) { return json.MarshalIndent(v, "", " ") } } return FormatFunc(func(r *Record) []byte { props := make(map[string]interface{}) props[r.KeyNames.Time] = r.Time props[r.KeyNames.Lvl] = r.Lvl.String() props[r.KeyNames.Msg] = r.Msg for i := 0; i < len(r.Ctx); i += 2 { k, ok := r.Ctx[i].(string) if !ok { props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i]) } props[k] = formatJsonValue(r.Ctx[i+1]) } b, err := jsonMarshal(props) if err != nil { b, _ = jsonMarshal(map[string]string{ errorKey: err.Error(), }) return b } if lineSeparated { b = append(b, '\n') } return b }) } func formatShared(value interface{}) (result interface{}) { defer func() { if err := recover(); err != nil { if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() { result = "nil" } else { panic(err) } } }() switch v := value.(type) { case time.Time: return v.Format(timeFormat) case error: return v.Error() case fmt.Stringer: return v.String() default: return v } } func formatJsonValue(value interface{}) interface{} { value = formatShared(value) switch value.(type) { case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string: return value default: return fmt.Sprintf("%+v", value) } } // formatValue formats a value for serialization func formatLogfmtValue(value interface{}) string { if value == nil { return "nil" } value = formatShared(value) switch v := value.(type) { case bool: return strconv.FormatBool(v) case float32: return strconv.FormatFloat(float64(v), floatFormat, 3, 64) case float64: return strconv.FormatFloat(v, floatFormat, 3, 64) case int, int8, int16, int32, int64, uint, uint16, uint32, uint64: return fmt.Sprintf("%d", value) case string: return escapeString(v) case uint8: return fmt.Sprintf("%X", value) case []byte: return fmt.Sprintf("%X", value) default: return escapeString(fmt.Sprintf("%+v", value)) } } func escapeString(s string) string { needQuotes := false e := bytes.Buffer{} e.WriteByte('"') for _, r := range s { if r <= ' ' || r == '=' || r == '"' { needQuotes = true } switch r { case '\\', '"': e.WriteByte('\\') e.WriteByte(byte(r)) case '\n': e.WriteByte('\\') e.WriteByte('n') case '\r': e.WriteByte('\\') e.WriteByte('r') case '\t': e.WriteByte('\\') e.WriteByte('t') default: e.WriteRune(r) } } e.WriteByte('"') start, stop := 0, e.Len() if !needQuotes { start, stop = 1, stop-1 } return string(e.Bytes()[start:stop]) } tendermint-log15-2.3-67-g9545b24/handler.go000066400000000000000000000240471271713054200177460ustar00rootroot00000000000000package log15 import ( "fmt" "io" "net" "os" "reflect" "sync" "github.com/go-stack/stack" ) // A Logger prints its log records by writing to a Handler. // The Handler interface defines where and how log records are written. // Handlers are composable, providing you great flexibility in combining // them to achieve the logging structure that suits your applications. type Handler interface { Log(r *Record) error } // FuncHandler returns a Handler that logs records with the given // function. func FuncHandler(fn func(r *Record) error) Handler { return funcHandler(fn) } type funcHandler func(r *Record) error func (h funcHandler) Log(r *Record) error { return h(r) } // StreamHandler writes log records to an io.Writer // with the given format. StreamHandler can be used // to easily begin writing log records to other // outputs. // // StreamHandler wraps itself with LazyHandler and SyncHandler // to evaluate Lazy objects and perform safe concurrent writes. func StreamHandler(wr io.Writer, fmtr Format) Handler { h := FuncHandler(func(r *Record) error { _, err := wr.Write(fmtr.Format(r)) return err }) return LazyHandler(SyncHandler(h)) } // SyncHandler can be wrapped around a handler to guarantee that // only a single Log operation can proceed at a time. It's necessary // for thread-safe concurrent writes. func SyncHandler(h Handler) Handler { var mu sync.Mutex return FuncHandler(func(r *Record) error { defer mu.Unlock() mu.Lock() return h.Log(r) }) } // FileHandler returns a handler which writes log records to the give file // using the given format. If the path // already exists, FileHandler will append to the given file. If it does not, // FileHandler will create the file with mode 0644. func FileHandler(path string, fmtr Format) (Handler, error) { f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { return nil, err } return closingHandler{f, StreamHandler(f, fmtr)}, nil } // NetHandler opens a socket to the given address and writes records // over the connection. func NetHandler(network, addr string, fmtr Format) (Handler, error) { conn, err := net.Dial(network, addr) if err != nil { return nil, err } return closingHandler{conn, StreamHandler(conn, fmtr)}, nil } // XXX: closingHandler is essentially unused at the moment // it's meant for a future time when the Handler interface supports // a possible Close() operation type closingHandler struct { io.WriteCloser Handler } func (h *closingHandler) Close() error { return h.WriteCloser.Close() } // CallerFileHandler returns a Handler that adds the line number and file of // the calling function to the context with key "caller". func CallerFileHandler(h Handler) Handler { return FuncHandler(func(r *Record) error { r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call)) return h.Log(r) }) } // CallerFuncHandler returns a Handler that adds the calling function name to // the context with key "fn". func CallerFuncHandler(h Handler) Handler { return FuncHandler(func(r *Record) error { r.Ctx = append(r.Ctx, "fn", fmt.Sprintf("%+n", r.Call)) return h.Log(r) }) } // CallerStackHandler returns a Handler that adds a stack trace to the context // with key "stack". The stack trace is formated as a space separated list of // call sites inside matching []'s. The most recent call site is listed first. // Each call site is formatted according to format. See the documentation of // package github.com/go-stack/stack for the list of supported formats. func CallerStackHandler(format string, h Handler) Handler { return FuncHandler(func(r *Record) error { s := stack.Trace().TrimBelow(r.Call).TrimRuntime() if len(s) > 0 { r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s)) } return h.Log(r) }) } // FilterHandler returns a Handler that only writes records to the // wrapped Handler if the given function evaluates true. For example, // to only log records where the 'err' key is not nil: // // logger.SetHandler(FilterHandler(func(r *Record) bool { // for i := 0; i < len(r.Ctx); i += 2 { // if r.Ctx[i] == "err" { // return r.Ctx[i+1] != nil // } // } // return false // }, h)) // func FilterHandler(fn func(r *Record) bool, h Handler) Handler { return FuncHandler(func(r *Record) error { if fn(r) { return h.Log(r) } return nil }) } // MatchFilterHandler returns a Handler that only writes records // to the wrapped Handler if the given key in the logged // context matches the value. For example, to only log records // from your ui package: // // log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler) // func MatchFilterHandler(key string, value interface{}, h Handler) Handler { return FilterHandler(func(r *Record) (pass bool) { switch key { case r.KeyNames.Lvl: return r.Lvl == value case r.KeyNames.Time: return r.Time == value case r.KeyNames.Msg: return r.Msg == value } for i := 0; i < len(r.Ctx); i += 2 { if r.Ctx[i] == key { return r.Ctx[i+1] == value } } return false }, h) } // LvlFilterHandler returns a Handler that only writes // records which are less than the given verbosity // level to the wrapped Handler. For example, to only // log Error/Crit records: // // log.LvlFilterHandler(log.Error, log.StdoutHandler) // func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { return FilterHandler(func(r *Record) (pass bool) { return r.Lvl <= maxLvl }, h) } // A MultiHandler dispatches any write to each of its handlers. // This is useful for writing different types of log information // to different locations. For example, to log to a file and // standard error: // // log.MultiHandler( // log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), // log.StderrHandler) // func MultiHandler(hs ...Handler) Handler { return FuncHandler(func(r *Record) error { for _, h := range hs { // what to do about failures? h.Log(r) } return nil }) } // A FailoverHandler writes all log records to the first handler // specified, but will failover and write to the second handler if // the first handler has failed, and so on for all handlers specified. // For example you might want to log to a network socket, but failover // to writing to a file if the network fails, and then to // standard out if the file write fails: // // log.FailoverHandler( // log.Must.NetHandler("tcp", ":9090", log.JsonFormat()), // log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), // log.StdoutHandler) // // All writes that do not go to the first handler will add context with keys of // the form "failover_err_{idx}" which explain the error encountered while // trying to write to the handlers before them in the list. func FailoverHandler(hs ...Handler) Handler { return FuncHandler(func(r *Record) error { var err error for i, h := range hs { err = h.Log(r) if err == nil { return nil } else { r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err) } } return err }) } // ChannelHandler writes all records to the given channel. // It blocks if the channel is full. Useful for async processing // of log messages, it's used by BufferedHandler. func ChannelHandler(recs chan<- *Record) Handler { return FuncHandler(func(r *Record) error { recs <- r return nil }) } // BufferedHandler writes all records to a buffered // channel of the given size which flushes into the wrapped // handler whenever it is available for writing. Since these // writes happen asynchronously, all writes to a BufferedHandler // never return an error and any errors from the wrapped handler are ignored. func BufferedHandler(bufSize int, h Handler) Handler { recs := make(chan *Record, bufSize) go func() { for m := range recs { _ = h.Log(m) } }() return ChannelHandler(recs) } // LazyHandler writes all values to the wrapped handler after evaluating // any lazy functions in the record's context. It is already wrapped // around StreamHandler and SyslogHandler in this library, you'll only need // it if you write your own Handler. func LazyHandler(h Handler) Handler { return FuncHandler(func(r *Record) error { // go through the values (odd indices) and reassign // the values of any lazy fn to the result of its execution hadErr := false for i := 1; i < len(r.Ctx); i += 2 { lz, ok := r.Ctx[i].(Lazy) if ok { v, err := evaluateLazy(lz) if err != nil { hadErr = true r.Ctx[i] = err } else { if cs, ok := v.(stack.CallStack); ok { v = cs.TrimBelow(r.Call).TrimRuntime() } r.Ctx[i] = v } } } if hadErr { r.Ctx = append(r.Ctx, errorKey, "bad lazy") } return h.Log(r) }) } func evaluateLazy(lz Lazy) (interface{}, error) { t := reflect.TypeOf(lz.Fn) if t.Kind() != reflect.Func { return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn) } if t.NumIn() > 0 { return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn) } if t.NumOut() == 0 { return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn) } value := reflect.ValueOf(lz.Fn) results := value.Call([]reflect.Value{}) if len(results) == 1 { return results[0].Interface(), nil } else { values := make([]interface{}, len(results)) for i, v := range results { values[i] = v.Interface() } return values, nil } } // DiscardHandler reports success for all writes but does nothing. // It is useful for dynamically disabling logging at runtime via // a Logger's SetHandler method. func DiscardHandler() Handler { return FuncHandler(func(r *Record) error { return nil }) } // The Must object provides the following Handler creation functions // which instead of returning an error parameter only return a Handler // and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler var Must muster func must(h Handler, err error) Handler { if err != nil { panic(err) } return h } type muster struct{} func (m muster) FileHandler(path string, fmtr Format) Handler { return must(FileHandler(path, fmtr)) } func (m muster) NetHandler(network, addr string, fmtr Format) Handler { return must(NetHandler(network, addr, fmtr)) } tendermint-log15-2.3-67-g9545b24/handler_go13.go000066400000000000000000000010011271713054200205600ustar00rootroot00000000000000// +build !go1.4 package log15 import ( "sync/atomic" "unsafe" ) // swapHandler wraps another handler that may be swapped out // dynamically at runtime in a thread-safe fashion. type swapHandler struct { handler unsafe.Pointer } func (h *swapHandler) Log(r *Record) error { return h.Get().Log(r) } func (h *swapHandler) Get() Handler { return *(*Handler)(atomic.LoadPointer(&h.handler)) } func (h *swapHandler) Swap(newHandler Handler) { atomic.StorePointer(&h.handler, unsafe.Pointer(&newHandler)) } tendermint-log15-2.3-67-g9545b24/handler_go14.go000066400000000000000000000007271271713054200205770ustar00rootroot00000000000000// +build go1.4 package log15 import "sync/atomic" // swapHandler wraps another handler that may be swapped out // dynamically at runtime in a thread-safe fashion. type swapHandler struct { handler atomic.Value } func (h *swapHandler) Log(r *Record) error { return (*h.handler.Load().(*Handler)).Log(r) } func (h *swapHandler) Swap(newHandler Handler) { h.handler.Store(&newHandler) } func (h *swapHandler) Get() Handler { return *h.handler.Load().(*Handler) } tendermint-log15-2.3-67-g9545b24/log15_test.go000066400000000000000000000272171271713054200203210ustar00rootroot00000000000000package log15 import ( "bufio" "bytes" "encoding/json" "errors" "fmt" "net" "regexp" "runtime" "sync" "testing" "time" ) func testHandler() (Handler, *Record) { rec := new(Record) return FuncHandler(func(r *Record) error { *rec = *r return nil }), rec } func testLogger() (Logger, Handler, *Record) { l := New() h, r := testHandler() l.SetHandler(LazyHandler(h)) return l, h, r } func TestLazy(t *testing.T) { t.Parallel() x := 1 lazy := func() int { return x } l, _, r := testLogger() l.Info("", "x", Lazy{lazy}) if r.Ctx[1] != 1 { t.Fatalf("Lazy function not evaluated, got %v, expected %d", r.Ctx[1], 1) } x = 2 l.Info("", "x", Lazy{lazy}) if r.Ctx[1] != 2 { t.Fatalf("Lazy function not evaluated, got %v, expected %d", r.Ctx[1], 1) } } func TestInvalidLazy(t *testing.T) { t.Parallel() l, _, r := testLogger() validate := func() { if len(r.Ctx) < 4 { t.Fatalf("Invalid lazy, got %d args, expecting at least 4", len(r.Ctx)) } if r.Ctx[2] != errorKey { t.Fatalf("Invalid lazy, got key %s expecting %s", r.Ctx[2], errorKey) } } l.Info("", "x", Lazy{1}) validate() l.Info("", "x", Lazy{func(x int) int { return x }}) validate() l.Info("", "x", Lazy{func() {}}) validate() } func TestCtx(t *testing.T) { t.Parallel() l, _, r := testLogger() l.Info("", Ctx{"x": 1, "y": "foo", "tester": t}) if len(r.Ctx) != 6 { t.Fatalf("Expecting Ctx tansformed into %d ctx args, got %d: %v", 6, len(r.Ctx), r.Ctx) } } func testFormatter(f Format) (Logger, *bytes.Buffer) { l := New() var buf bytes.Buffer l.SetHandler(StreamHandler(&buf, f)) return l, &buf } func TestJson(t *testing.T) { t.Parallel() l, buf := testFormatter(JsonFormat()) l.Error("some message", "x", 1, "y", 3.2) var v map[string]interface{} decoder := json.NewDecoder(buf) if err := decoder.Decode(&v); err != nil { t.Fatalf("Error decoding JSON: %v", v) } validate := func(key string, expected interface{}) { if v[key] != expected { t.Fatalf("Got %v expected %v for %v", v[key], expected, key) } } validate("msg", "some message") validate("x", float64(1)) // all numbers are floats in JSON land validate("y", 3.2) validate("lvl", "eror") } type testtype struct { name string } func (tt testtype) String() string { return tt.name } func TestLogfmt(t *testing.T) { t.Parallel() var nilVal *testtype l, buf := testFormatter(LogfmtFormat()) l.Error("some message", "x", 1, "y", 3.2, "equals", "=", "quote", "\"", "nil", nilVal) // skip timestamp in comparison got := buf.Bytes()[27:buf.Len()] expected := []byte(`lvl=eror msg="some message" x=1 y=3.200 equals="=" quote="\"" nil=nil` + "\n") if !bytes.Equal(got, expected) { t.Fatalf("Got %s, expected %s", got, expected) } } func TestMultiHandler(t *testing.T) { t.Parallel() h1, r1 := testHandler() h2, r2 := testHandler() l := New() l.SetHandler(MultiHandler(h1, h2)) l.Debug("clone") if r1.Msg != "clone" { t.Fatalf("wrong value for h1.Msg. Got %s expected %s", r1.Msg, "clone") } if r2.Msg != "clone" { t.Fatalf("wrong value for h2.Msg. Got %s expected %s", r2.Msg, "clone") } } type waitHandler struct { ch chan Record } func (h *waitHandler) Log(r *Record) error { h.ch <- *r return nil } func TestBufferedHandler(t *testing.T) { t.Parallel() ch := make(chan Record) l := New() l.SetHandler(BufferedHandler(0, &waitHandler{ch})) l.Debug("buffer") if r := <-ch; r.Msg != "buffer" { t.Fatalf("wrong value for r.Msg. Got %s expected %s", r.Msg, "") } } func TestLogContext(t *testing.T) { t.Parallel() l, _, r := testLogger() l = l.New("foo", "bar") l.Crit("baz") if len(r.Ctx) != 2 { t.Fatalf("Expected logger context in record context. Got length %d, expected %d", len(r.Ctx), 2) } if r.Ctx[0] != "foo" { t.Fatalf("Wrong context key, got %s expected %s", r.Ctx[0], "foo") } if r.Ctx[1] != "bar" { t.Fatalf("Wrong context value, got %s expected %s", r.Ctx[1], "bar") } } func TestMapCtx(t *testing.T) { t.Parallel() l, _, r := testLogger() l.Crit("test", Ctx{"foo": "bar"}) if len(r.Ctx) != 2 { t.Fatalf("Wrong context length, got %d, expected %d", len(r.Ctx), 2) } if r.Ctx[0] != "foo" { t.Fatalf("Wrong context key, got %s expected %s", r.Ctx[0], "foo") } if r.Ctx[1] != "bar" { t.Fatalf("Wrong context value, got %s expected %s", r.Ctx[1], "bar") } } func TestLvlFilterHandler(t *testing.T) { t.Parallel() l := New() h, r := testHandler() l.SetHandler(LvlFilterHandler(LvlWarn, h)) l.Info("info'd") if r.Msg != "" { t.Fatalf("Expected zero record, but got record with msg: %v", r.Msg) } l.Warn("warned") if r.Msg != "warned" { t.Fatalf("Got record msg %s expected %s", r.Msg, "warned") } l.Warn("error'd") if r.Msg != "error'd" { t.Fatalf("Got record msg %s expected %s", r.Msg, "error'd") } } func TestNetHandler(t *testing.T) { t.Parallel() l, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to listen: %v", l) } errs := make(chan error) go func() { c, err := l.Accept() if err != nil { t.Errorf("Failed to accept conneciton: %v", err) return } rd := bufio.NewReader(c) s, err := rd.ReadString('\n') if err != nil { t.Errorf("Failed to read string: %v", err) } got := s[27:] expected := "lvl=info msg=test x=1\n" if got != expected { t.Errorf("Got log line %s, expected %s", got, expected) } errs <- nil }() lg := New() h, err := NetHandler("tcp", l.Addr().String(), LogfmtFormat()) if err != nil { t.Fatal(err) } lg.SetHandler(h) lg.Info("test", "x", 1) select { case <-time.After(time.Second): t.Fatalf("Test timed out!") case <-errs: // ok } } func TestMatchFilterHandler(t *testing.T) { t.Parallel() l, h, r := testLogger() l.SetHandler(MatchFilterHandler("err", nil, h)) l.Crit("test", "foo", "bar") if r.Msg != "" { t.Fatalf("expected filter handler to discard msg") } l.Crit("test2", "err", "bad fd") if r.Msg != "" { t.Fatalf("expected filter handler to discard msg") } l.Crit("test3", "err", nil) if r.Msg != "test3" { t.Fatalf("expected filter handler to allow msg") } } func TestMatchFilterBuiltin(t *testing.T) { t.Parallel() l, h, r := testLogger() l.SetHandler(MatchFilterHandler("lvl", LvlError, h)) l.Info("does not pass") if r.Msg != "" { t.Fatalf("got info level record that should not have matched") } l.Error("error!") if r.Msg != "error!" { t.Fatalf("did not get error level record that should have matched") } r.Msg = "" l.SetHandler(MatchFilterHandler("msg", "matching message", h)) l.Info("doesn't match") if r.Msg != "" { t.Fatalf("got record with wrong message matched") } l.Debug("matching message") if r.Msg != "matching message" { t.Fatalf("did not get record which matches") } } type failingWriter struct { fail bool } func (w *failingWriter) Write(buf []byte) (int, error) { if w.fail { return 0, errors.New("fail") } else { return len(buf), nil } } func TestFailoverHandler(t *testing.T) { t.Parallel() l := New() h, r := testHandler() w := &failingWriter{false} l.SetHandler(FailoverHandler( StreamHandler(w, JsonFormat()), h)) l.Debug("test ok") if r.Msg != "" { t.Fatalf("expected no failover") } w.fail = true l.Debug("test failover", "x", 1) if r.Msg != "test failover" { t.Fatalf("expected failover") } if len(r.Ctx) != 4 { t.Fatalf("expected additional failover ctx") } got := r.Ctx[2] expected := "failover_err_0" if got != expected { t.Fatalf("expected failover ctx. got: %s, expected %s", got, expected) } } // https://github.com/inconshreveable/log15/issues/16 func TestIndependentSetHandler(t *testing.T) { t.Parallel() parent, _, r := testLogger() child := parent.New() child.SetHandler(DiscardHandler()) parent.Info("test") if r.Msg != "test" { t.Fatalf("parent handler affected by child") } } // https://github.com/inconshreveable/log15/issues/16 func TestInheritHandler(t *testing.T) { t.Parallel() parent, _, r := testLogger() child := parent.New() parent.SetHandler(DiscardHandler()) child.Info("test") if r.Msg == "test" { t.Fatalf("child handler affected not affected by parent") } } func TestCallerFileHandler(t *testing.T) { t.Parallel() l := New() h, r := testHandler() l.SetHandler(CallerFileHandler(h)) l.Info("baz") _, _, line, _ := runtime.Caller(0) if len(r.Ctx) != 2 { t.Fatalf("Expected caller in record context. Got length %d, expected %d", len(r.Ctx), 2) } const key = "caller" if r.Ctx[0] != key { t.Fatalf("Wrong context key, got %s expected %s", r.Ctx[0], key) } s, ok := r.Ctx[1].(string) if !ok { t.Fatalf("Wrong context value type, got %T expected string", r.Ctx[1]) } exp := fmt.Sprint("log15_test.go:", line-1) if s != exp { t.Fatalf("Wrong context value, got %s expected string matching %s", s, exp) } } func TestCallerFuncHandler(t *testing.T) { t.Parallel() l := New() h, r := testHandler() l.SetHandler(CallerFuncHandler(h)) l.Info("baz") if len(r.Ctx) != 2 { t.Fatalf("Expected caller in record context. Got length %d, expected %d", len(r.Ctx), 2) } const key = "fn" if r.Ctx[0] != key { t.Fatalf("Wrong context key, got %s expected %s", r.Ctx[0], key) } const regex = ".*\\.TestCallerFuncHandler" s, ok := r.Ctx[1].(string) if !ok { t.Fatalf("Wrong context value type, got %T expected string", r.Ctx[1]) } match, err := regexp.MatchString(regex, s) if err != nil { t.Fatalf("Error matching %s to regex %s: %v", s, regex, err) } if !match { t.Fatalf("Wrong context value, got %s expected string matching %s", s, regex) } } // https://github.com/inconshreveable/log15/issues/27 func TestCallerStackHandler(t *testing.T) { t.Parallel() l := New() h, r := testHandler() l.SetHandler(CallerStackHandler("%#v", h)) lines := []int{} func() { l.Info("baz") _, _, line, _ := runtime.Caller(0) lines = append(lines, line-1) }() _, file, line, _ := runtime.Caller(0) lines = append(lines, line-1) if len(r.Ctx) != 2 { t.Fatalf("Expected stack in record context. Got length %d, expected %d", len(r.Ctx), 2) } const key = "stack" if r.Ctx[0] != key { t.Fatalf("Wrong context key, got %s expected %s", r.Ctx[0], key) } s, ok := r.Ctx[1].(string) if !ok { t.Fatalf("Wrong context value type, got %T expected string", r.Ctx[1]) } exp := "[" for i, line := range lines { if i > 0 { exp += " " } exp += fmt.Sprint(file, ":", line) } exp += "]" if s != exp { t.Fatalf("Wrong context value, got %s expected string matching %s", s, exp) } } // tests that when logging concurrently to the same logger // from multiple goroutines that the calls are handled independently // this test tries to trigger a previous bug where concurrent calls could // corrupt each other's context values // // this test runs N concurrent goroutines each logging a fixed number of // records and a handler that buckets them based on the index passed in the context. // if the logger is not concurrent-safe then the values in the buckets will not all be the same // // https://github.com/inconshreveable/log15/pull/30 func TestConcurrent(t *testing.T) { root := New() // this was the first value that triggered // go to allocate extra capacity in the logger's context slice which // was necessary to trigger the bug const ctxLen = 34 l := root.New(make([]interface{}, ctxLen)...) const goroutines = 8 var res [goroutines]int l.SetHandler(SyncHandler(FuncHandler(func(r *Record) error { res[r.Ctx[ctxLen+1].(int)]++ return nil }))) var wg sync.WaitGroup wg.Add(goroutines) for i := 0; i < goroutines; i++ { go func(idx int) { defer wg.Done() for j := 0; j < 10000; j++ { l.Info("test message", "goroutine_idx", idx) } }(i) } wg.Wait() for _, val := range res[:] { if val != 10000 { t.Fatalf("Wrong number of messages for context: %+v", res) } } } tendermint-log15-2.3-67-g9545b24/logger.go000066400000000000000000000114271271713054200176060ustar00rootroot00000000000000package log15 import ( "fmt" "time" "github.com/go-stack/stack" ) const timeKey = "t" const lvlKey = "lvl" const msgKey = "msg" const errorKey = "LOG15_ERROR" type Lvl int const ( LvlCrit Lvl = iota LvlError LvlWarn LvlNotice LvlInfo LvlDebug ) // Returns the name of a Lvl func (l Lvl) String() string { switch l { case LvlDebug: return "dbug" case LvlInfo: return "info" case LvlNotice: return "note" case LvlWarn: return "warn" case LvlError: return "eror" case LvlCrit: return "crit" default: panic("bad level") } } // Returns the appropriate Lvl from a string name. // Useful for parsing command line args and configuration files. func LvlFromString(lvlString string) (Lvl, error) { switch lvlString { case "debug", "dbug": return LvlDebug, nil case "info": return LvlInfo, nil case "note", "notice": return LvlNotice, nil case "warn": return LvlWarn, nil case "error", "eror": return LvlError, nil case "crit": return LvlCrit, nil default: return LvlDebug, fmt.Errorf("Unknown level: %v", lvlString) } } // A Record is what a Logger asks its handler to write type Record struct { Time time.Time Lvl Lvl Msg string Ctx []interface{} Call stack.Call KeyNames RecordKeyNames } type RecordKeyNames struct { Time string Msg string Lvl string } // A Logger writes key/value pairs to a Handler type Logger interface { // New returns a new Logger that has this logger's context plus the given context New(ctx ...interface{}) Logger // GetHandler gets the handler associated with the logger. GetHandler() Handler // SetHandler updates the logger to write records to the specified handler. SetHandler(h Handler) // Log a message at the given level with context key/value pairs Debug(msg string, ctx ...interface{}) Info(msg string, ctx ...interface{}) Notice(msg string, ctx ...interface{}) Warn(msg string, ctx ...interface{}) Error(msg string, ctx ...interface{}) Crit(msg string, ctx ...interface{}) } type logger struct { ctx []interface{} h *swapHandler } func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) { l.h.Log(&Record{ Time: time.Now(), Lvl: lvl, Msg: msg, Ctx: newContext(l.ctx, ctx), Call: stack.Caller(2), KeyNames: RecordKeyNames{ Time: timeKey, Msg: msgKey, Lvl: lvlKey, }, }) } func (l *logger) New(ctx ...interface{}) Logger { child := &logger{newContext(l.ctx, ctx), new(swapHandler)} child.SetHandler(l.h) return child } func newContext(prefix []interface{}, suffix []interface{}) []interface{} { normalizedSuffix := normalize(suffix) newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix)) n := copy(newCtx, prefix) copy(newCtx[n:], normalizedSuffix) return newCtx } func (l *logger) Debug(msg string, ctx ...interface{}) { l.write(msg, LvlDebug, ctx) } func (l *logger) Info(msg string, ctx ...interface{}) { l.write(msg, LvlInfo, ctx) } func (l *logger) Notice(msg string, ctx ...interface{}) { l.write(msg, LvlNotice, ctx) } func (l *logger) Warn(msg string, ctx ...interface{}) { l.write(msg, LvlWarn, ctx) } func (l *logger) Error(msg string, ctx ...interface{}) { l.write(msg, LvlError, ctx) } func (l *logger) Crit(msg string, ctx ...interface{}) { l.write(msg, LvlCrit, ctx) } func (l *logger) GetHandler() Handler { return l.h.Get() } func (l *logger) SetHandler(h Handler) { l.h.Swap(h) } func normalize(ctx []interface{}) []interface{} { // if the caller passed a Ctx object, then expand it if len(ctx) == 1 { if ctxMap, ok := ctx[0].(Ctx); ok { ctx = ctxMap.toArray() } } // ctx needs to be even because it's a series of key/value pairs // no one wants to check for errors on logging functions, // so instead of erroring on bad input, we'll just make sure // that things are the right length and users can fix bugs // when they see the output looks wrong if len(ctx)%2 != 0 { ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil") } return ctx } // Lazy allows you to defer calculation of a logged value that is expensive // to compute until it is certain that it must be evaluated with the given filters. // // Lazy may also be used in conjunction with a Logger's New() function // to generate a child logger which always reports the current value of changing // state. // // You may wrap any function which takes no arguments to Lazy. It may return any // number of values of any type. type Lazy struct { Fn interface{} } // Ctx is a map of key/value pairs to pass as context to a log function // Use this only if you really need greater safety around the arguments you pass // to the logging functions. type Ctx map[string]interface{} func (c Ctx) toArray() []interface{} { arr := make([]interface{}, len(c)*2) i := 0 for k, v := range c { arr[i] = k arr[i+1] = v i += 2 } return arr } tendermint-log15-2.3-67-g9545b24/root.go000066400000000000000000000034071271713054200173110ustar00rootroot00000000000000package log15 import ( "os" "github.com/mattn/go-colorable" "github.com/tendermint/log15/term" ) var ( root *logger StdoutHandler = StreamHandler(os.Stdout, LogfmtFormat()) StderrHandler = StreamHandler(os.Stderr, LogfmtFormat()) ) func init() { if term.IsTty(os.Stdout.Fd()) { StdoutHandler = StreamHandler(colorable.NewColorableStdout(), TerminalFormat()) } if term.IsTty(os.Stderr.Fd()) { StderrHandler = StreamHandler(colorable.NewColorableStderr(), TerminalFormat()) } root = &logger{[]interface{}{}, new(swapHandler)} root.SetHandler(StdoutHandler) } // New returns a new logger with the given context. // New is a convenient alias for Root().New func New(ctx ...interface{}) Logger { return root.New(ctx...) } // Root returns the root logger func Root() Logger { return root } // The following functions bypass the exported logger methods (logger.Debug, // etc.) to keep the call depth the same for all paths to logger.write so // runtime.Caller(2) always refers to the call site in client code. // Debug is a convenient alias for Root().Debug func Debug(msg string, ctx ...interface{}) { root.write(msg, LvlDebug, ctx) } // Info is a convenient alias for Root().Info func Info(msg string, ctx ...interface{}) { root.write(msg, LvlInfo, ctx) } // Notice is a convenient alias for Root().Notice func Notice(msg string, ctx ...interface{}) { root.write(msg, LvlNotice, ctx) } // Warn is a convenient alias for Root().Warn func Warn(msg string, ctx ...interface{}) { root.write(msg, LvlWarn, ctx) } // Error is a convenient alias for Root().Error func Error(msg string, ctx ...interface{}) { root.write(msg, LvlError, ctx) } // Crit is a convenient alias for Root().Crit func Crit(msg string, ctx ...interface{}) { root.write(msg, LvlCrit, ctx) } tendermint-log15-2.3-67-g9545b24/syslog.go000066400000000000000000000030621271713054200176430ustar00rootroot00000000000000// +build !windows,!plan9 package log15 import ( "log/syslog" "strings" ) // SyslogHandler opens a connection to the system syslog daemon by calling // syslog.New and writes all records to it. func SyslogHandler(priority syslog.Priority, tag string, fmtr Format) (Handler, error) { wr, err := syslog.New(priority, tag) return sharedSyslog(fmtr, wr, err) } // SyslogHandler opens a connection to a log daemon over the network and writes // all log records to it. func SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) (Handler, error) { wr, err := syslog.Dial(net, addr, priority, tag) return sharedSyslog(fmtr, wr, err) } func sharedSyslog(fmtr Format, sysWr *syslog.Writer, err error) (Handler, error) { if err != nil { return nil, err } h := FuncHandler(func(r *Record) error { var syslogFn = sysWr.Info switch r.Lvl { case LvlCrit: syslogFn = sysWr.Crit case LvlError: syslogFn = sysWr.Err case LvlWarn: syslogFn = sysWr.Warning case LvlNotice: syslogFn = sysWr.Notice case LvlInfo: syslogFn = sysWr.Info case LvlDebug: syslogFn = sysWr.Debug } s := strings.TrimSpace(string(fmtr.Format(r))) return syslogFn(s) }) return LazyHandler(&closingHandler{sysWr, h}), nil } func (m muster) SyslogHandler(priority syslog.Priority, tag string, fmtr Format) Handler { return must(SyslogHandler(priority, tag, fmtr)) } func (m muster) SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) Handler { return must(SyslogNetHandler(net, addr, priority, tag, fmtr)) } tendermint-log15-2.3-67-g9545b24/term/000077500000000000000000000000001271713054200167425ustar00rootroot00000000000000tendermint-log15-2.3-67-g9545b24/term/LICENSE000066400000000000000000000020721271713054200177500ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Simon Eskildsen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tendermint-log15-2.3-67-g9545b24/term/terminal_appengine.go000066400000000000000000000004671271713054200231410ustar00rootroot00000000000000// Based on ssh/terminal: // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build appengine package term // IsTty always returns false on AppEngine. func IsTty(fd uintptr) bool { return false } tendermint-log15-2.3-67-g9545b24/term/terminal_darwin.go000066400000000000000000000004421271713054200224500ustar00rootroot00000000000000// Based on ssh/terminal: // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package term import "syscall" const ioctlReadTermios = syscall.TIOCGETA type Termios syscall.Termios tendermint-log15-2.3-67-g9545b24/term/terminal_freebsd.go000066400000000000000000000005161271713054200226000ustar00rootroot00000000000000package term import ( "syscall" ) const ioctlReadTermios = syscall.TIOCGETA // Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin. type Termios struct { Iflag uint32 Oflag uint32 Cflag uint32 Lflag uint32 Cc [20]uint8 Ispeed uint32 Ospeed uint32 } tendermint-log15-2.3-67-g9545b24/term/terminal_linux.go000066400000000000000000000004661271713054200223310ustar00rootroot00000000000000// Based on ssh/terminal: // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build !appengine package term import "syscall" const ioctlReadTermios = syscall.TCGETS type Termios syscall.Termios tendermint-log15-2.3-67-g9545b24/term/terminal_notwindows.go000066400000000000000000000010301271713054200233710ustar00rootroot00000000000000// Based on ssh/terminal: // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build linux,!appengine darwin freebsd openbsd package term import ( "syscall" "unsafe" ) // IsTty returns true if the given file descriptor is a terminal. func IsTty(fd uintptr) bool { var termios Termios _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) return err == 0 } tendermint-log15-2.3-67-g9545b24/term/terminal_openbsd.go000066400000000000000000000001571271713054200226210ustar00rootroot00000000000000package term import "syscall" const ioctlReadTermios = syscall.TIOCGETA type Termios syscall.Termios tendermint-log15-2.3-67-g9545b24/term/terminal_windows.go000066400000000000000000000011321271713054200226530ustar00rootroot00000000000000// Based on ssh/terminal: // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build windows package term import ( "syscall" "unsafe" ) var kernel32 = syscall.NewLazyDLL("kernel32.dll") var ( procGetConsoleMode = kernel32.NewProc("GetConsoleMode") ) // IsTty returns true if the given file descriptor is a terminal. func IsTty(fd uintptr) bool { var st uint32 r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) return r != 0 && e == 0 }