[
  {
    "path": ".travis.yml",
    "content": "language: go\ngo:\n- 1.7\n- 1.8\n- tip\nmatrix:\n  allow_failures:\n      - go: tip\nscript:\n    go test -v -race -cpu=1,2,4 ./...\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2015 Olivier Poitrey <rs@dailymotion.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished\nto do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": ":warning: **Check [zerolog](https://github.com/rs/zerolog), the successor of xlog.**\n\n\n# HTTP Handler Logger\n\n[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/xlog) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/xlog/master/LICENSE) [![Build Status](https://travis-ci.org/rs/xlog.svg?branch=master)](https://travis-ci.org/rs/xlog) [![Coverage](http://gocover.io/_badge/github.com/rs/xlog)](http://gocover.io/github.com/rs/xlog)\n\n`xlog` is a logger for [net/context](https://godoc.org/golang.org/x/net/context) aware HTTP applications.\n\nUnlike most loggers, `xlog` will never block your application because one its outputs is lagging. The log commands are connected to their outputs through a buffered channel and will prefer to discard messages if the buffer get full. All message formatting, serialization and transport happen in a dedicated go routine.\n\nRead more about `xlog` on [Dailymotion engineering blog](http://engineering.dailymotion.com/our-way-to-go/).\n\n![](screenshot.png)\n\n## Features\n\n- Per request log context\n- Per request and/or per message key/value fields\n- Log levels (Debug, Info, Warn, Error)\n- Color output when terminal is detected\n- Custom output (JSON, [logfmt](https://github.com/kr/logfmt), …)\n- Automatic gathering of request context like User-Agent, IP etc.\n- Drops message rather than blocking execution\n- Easy access logging thru [github.com/rs/xaccess](https://github.com/rs/xaccess)\n\nWorks with both Go 1.7+ (with `net/context` support) and Go 1.6 if used with [github.com/rs/xhandler](https://github.com/rs/xhandler).\n\n## Install\n\n    go get github.com/rs/xlog\n\n## Usage\n\n```go\nc := alice.New()\n\nhost, _ := os.Hostname()\nconf := xlog.Config{\n    // Log info level and higher\n    Level: xlog.LevelInfo,\n    // Set some global env fields\n    Fields: xlog.F{\n        \"role\": \"my-service\",\n        \"host\": host,\n    },\n    // Output everything on console\n    Output: xlog.NewOutputChannel(xlog.NewConsoleOutput()),\n}\n\n// Install the logger handler\nc = c.Append(xlog.NewHandler(conf))\n\n// Optionally plug the xlog handler's input to Go's default logger\nlog.SetFlags(0)\nxlogger := xlog.New(conf)\nlog.SetOutput(xlogger)\n\n// Install some provided extra handler to set some request's context fields.\n// Thanks to those handler, all our logs will come with some pre-populated fields.\nc = c.Append(xlog.MethodHandler(\"method\"))\nc = c.Append(xlog.URLHandler(\"url\"))\nc = c.Append(xlog.RemoteAddrHandler(\"ip\"))\nc = c.Append(xlog.UserAgentHandler(\"user_agent\"))\nc = c.Append(xlog.RefererHandler(\"referer\"))\nc = c.Append(xlog.RequestIDHandler(\"req_id\", \"Request-Id\"))\n\n// Here is your final handler\nh := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n    // Get the logger from the request's context. You can safely assume it\n    // will be always there: if the handler is removed, xlog.FromContext\n    // will return a NopLogger\n    l := xlog.FromRequest(r)\n\n    // Then log some errors\n    if err := errors.New(\"some error from elsewhere\"); err != nil {\n        l.Errorf(\"Here is an error: %v\", err)\n    }\n\n    // Or some info with fields\n    l.Info(\"Something happend\", xlog.F{\n        \"user\":   \"current user id\",\n        \"status\": \"ok\",\n    })\n    // Output:\n    // {\n    //   \"message\": \"Something happend\",\n    //   \"level\": \"info\",\n    //   \"file\": \"main.go:34\",\n    //   \"time\": time.Time{...},\n    //   \"user\": \"current user id\",\n    //   \"status\": \"ok\",\n    //   \"ip\": \"1.2.3.4\",\n    //   \"user-agent\": \"Mozilla/1.2.3...\",\n    //   \"referer\": \"http://somewhere.com/path\",\n    //   \"role\": \"my-service\",\n    //   \"host\": \"somehost\"\n    // }\n}))\nhttp.Handle(\"/\", h)\n\nif err := http.ListenAndServe(\":8080\", nil); err != nil {\n    xlogger.Fatal(err)\n}\n```\n\n### Copy Logger\n\nYou may want to get a copy of the current logger to pass a modified version to a function without touching the original:\n\n```go\nl := xlog.FromContext(ctx)\nl2 := xlog.Copy(l)\nl2.SetField(\"foo\", \"bar\")\n```\n\nMake sure you copy a request context logger if you plan to use it in a go routine that may still exist after the end of the current request. Contextual loggers are reused after each requests to lower the pressure on the garbage collector. If you would use such a logger in a go routine, you may end up using a logger from another request/context or worse, a nil pointer:\n\n```go\nl := xlog.FromContext(ctx)\nl2 := xlog.Copy(l)\ngo func() {\n    // use the safe copy\n    l2.Info(\"something\")\n}()\n```\n\n### Global Logger\n\nYou may use the standard Go logger and plug `xlog` as it's output as `xlog` implements `io.Writer`:\n\n```go\nxlogger := xlog.New(conf)\nlog.SetOutput(xlogger)\n```\n\nThis has the advantage to make all your existing code or libraries already using Go's standard logger to use `xlog` with no change. The drawback though, is that you won't have control on the logging level and won't be able to add custom fields (other than ones set on the logger itself via configuration or `SetFields()`) for those messages.\n\nAnother option for code you manage but which is outside of a HTTP request handler is to use the `xlog` provided default logger:\n\n```go\nxlog.Debugf(\"some message with %s\", variable, xlog.F{\"and\": \"field support\"})\n```\n\nThis way you have access to all the possibilities offered by `xlog` without having to carry the logger instance around. The default global logger has no fields set and has its output set to the console with no buffering channel. You may want to change that using the `xlog.SetLogger()` method:\n\n```go\nxlog.SetLogger(xlog.New(xlog.Config{\n    Level: xlog.LevelInfo,\n    Output: xlog.NewConsoleOutput(),\n    Fields: xlog.F{\n        \"role\": \"my-service\",\n    },\n}))\n```\n\n### Configure Output\n\nBy default, output is setup to output debug and info message on `STDOUT` and warning and errors to `STDERR`. You can easily change this setup.\n\nXLog output can be customized using composable output handlers. Thanks to the [LevelOutput](https://godoc.org/github.com/rs/xlog#LevelOutput), [MultiOutput](https://godoc.org/github.com/rs/xlog#MultiOutput) and [FilterOutput](https://godoc.org/github.com/rs/xlog#FilterOutput), it is easy to route messages precisely.\n\n```go\nconf := xlog.Config{\n    Output: xlog.NewOutputChannel(xlog.MultiOutput{\n        // Send all logs with field type=mymodule to a remote syslog\n        0: xlog.FilterOutput{\n            Cond: func(fields map[string]interface{}) bool {\n                return fields[\"type\"] == \"mymodule\"\n            },\n            Output: xlog.NewSyslogOutput(\"tcp\", \"1.2.3.4:1234\", \"mymodule\"),\n        },\n        // Setup different output per log level\n        1: xlog.LevelOutput{\n            // Send errors to the console\n            Error: xlog.NewConsoleOutput(),\n            // Send syslog output for error level\n            Info: xlog.NewSyslogOutput(\"\", \"\", \"\"),\n        },\n    }),\n})\n\nh = xlog.NewHandler(conf)\n```\n\n#### Built-in Output Modules\n\n| Name | Description |\n|------|-------------|\n| [OutputChannel](https://godoc.org/github.com/rs/xlog#OutputChannel) | Buffers messages before sending. This output should always be the output directly set to xlog's configuration.\n| [MultiOutput](https://godoc.org/github.com/rs/xlog#MultiOutput) | Routes the same message to several outputs. If one or more outputs return error, the last error is returned.\n| [FilterOutput](https://godoc.org/github.com/rs/xlog#FilterOutput) | Tests a condition on the message and forward it to the child output if true.\n| [LevelOutput](https://godoc.org/github.com/rs/xlog#LevelOutput) | Routes messages per level outputs.\n| [ConsoleOutput](https://godoc.org/github.com/rs/xlog#NewConsoleOutput) | Prints messages in a human readable form on the stdout with color when supported. Fallback to logfmt output if the stdout isn't a terminal.\n| [JSONOutput](https://godoc.org/github.com/rs/xlog#NewJSONOutput) | Serialize messages in JSON.\n| [LogfmtOutput](https://godoc.org/github.com/rs/xlog#NewLogfmtOutput) | Serialize messages using Heroku like [logfmt](https://github.com/kr/logfmt).\n| [LogstashOutput](https://godoc.org/github.com/rs/xlog#NewLogstashOutput) | Serialize JSON message using Logstash 2.0 (schema v1) structured format.\n| [SyslogOutput](https://godoc.org/github.com/rs/xlog#NewSyslogOutput) | Send messages to syslog.\n| [UIDOutput](https://godoc.org/github.com/rs/xlog#NewUIDOutput) | Append a globally unique id to every message and forward it to the next output.\n\n## Third Party Extensions\n\n| Project | Author | Description |\n|---------|--------|-------------|\n| [gRPClog](https://github.com/clawio/grpcxlog) | [Hugo González Labrador](https://github.com/labkode) | An adapter to use xlog as the logger for grpclog.\n| [xlog-nsq](https://github.com/rs/xlog-nsq) | [Olivier Poitrey](https://github.com/rs) | An xlog to [NSQ](http://nsq.io) output.\n| [xlog-sentry](https://github.com/trong/xlog-sentry) | [trong](https://github.com/trong) | An xlog to [Sentry](https://getsentry.com/) output.\n\n## Licenses\n\nAll source code is licensed under the [MIT License](https://raw.github.com/rs/xlog/master/LICENSE).\n"
  },
  {
    "path": "handler.go",
    "content": "// +build go1.7\n\npackage xlog\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/http\"\n\n\t\"github.com/rs/xid\"\n)\n\ntype key int\n\nconst (\n\tlogKey key = iota\n\tidKey\n)\n\n// IDFromContext returns the unique id associated to the request if any.\nfunc IDFromContext(ctx context.Context) (xid.ID, bool) {\n\tid, ok := ctx.Value(idKey).(xid.ID)\n\treturn id, ok\n}\n\n// IDFromRequest returns the unique id accociated to the request if any.\nfunc IDFromRequest(r *http.Request) (xid.ID, bool) {\n\tif r == nil {\n\t\treturn xid.ID{}, false\n\t}\n\treturn IDFromContext(r.Context())\n}\n\n// FromContext gets the logger out of the context.\n// If not logger is stored in the context, a NopLogger is returned.\nfunc FromContext(ctx context.Context) Logger {\n\tif ctx == nil {\n\t\treturn NopLogger\n\t}\n\tl, ok := ctx.Value(logKey).(Logger)\n\tif !ok {\n\t\treturn NopLogger\n\t}\n\treturn l\n}\n\n// FromRequest gets the logger in the request's context.\n// This is a shortcut for xlog.FromContext(r.Context())\nfunc FromRequest(r *http.Request) Logger {\n\tif r == nil {\n\t\treturn NopLogger\n\t}\n\treturn FromContext(r.Context())\n}\n\n// NewContext returns a copy of the parent context and associates it with the provided logger.\nfunc NewContext(ctx context.Context, l Logger) context.Context {\n\treturn context.WithValue(ctx, logKey, l)\n}\n\n// NewHandler instanciates a new xlog HTTP handler.\n//\n// If not configured, the output is set to NewConsoleOutput() by default.\nfunc NewHandler(c Config) func(http.Handler) http.Handler {\n\tif c.Output == nil {\n\t\tc.Output = NewOutputChannel(NewConsoleOutput())\n\t}\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tvar l Logger\n\t\t\tif r != nil {\n\t\t\t\tl = New(c)\n\t\t\t\tr = r.WithContext(NewContext(r.Context(), l))\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t\tif l, ok := l.(*logger); ok {\n\t\t\t\tl.close()\n\t\t\t}\n\t\t})\n\t}\n}\n\n// URLHandler returns a handler setting the request's URL as a field\n// to the current context's logger using the passed name as field name.\nfunc URLHandler(name string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tl := FromContext(r.Context())\n\t\t\tl.SetField(name, r.URL.String())\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// MethodHandler returns a handler setting the request's method as a field\n// to the current context's logger using the passed name as field name.\nfunc MethodHandler(name string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tl := FromContext(r.Context())\n\t\t\tl.SetField(name, r.Method)\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// RequestHandler returns a handler setting the request's method and URL as a field\n// to the current context's logger using the passed name as field name.\nfunc RequestHandler(name string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tl := FromContext(r.Context())\n\t\t\tl.SetField(name, r.Method+\" \"+r.URL.String())\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// RemoteAddrHandler returns a handler setting the request's remote address as a field\n// to the current context's logger using the passed name as field name.\nfunc RemoteAddrHandler(name string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tif host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {\n\t\t\t\tl := FromContext(r.Context())\n\t\t\t\tl.SetField(name, host)\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// UserAgentHandler returns a handler setting the request's client's user-agent as\n// a field to the current context's logger using the passed name as field name.\nfunc UserAgentHandler(name string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tif ua := r.Header.Get(\"User-Agent\"); ua != \"\" {\n\t\t\t\tl := FromContext(r.Context())\n\t\t\t\tl.SetField(name, ua)\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// RefererHandler returns a handler setting the request's referer header as\n// a field to the current context's logger using the passed name as field name.\nfunc RefererHandler(name string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tif ref := r.Header.Get(\"Referer\"); ref != \"\" {\n\t\t\t\tl := FromContext(r.Context())\n\t\t\t\tl.SetField(name, ref)\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// RequestIDHandler returns a handler setting a unique id to the request which can\n// be gathered using IDFromContext(ctx). This generated id is added as a field to the\n// logger using the passed name as field name. The id is also added as a response\n// header if the headerName is not empty.\n//\n// The generated id is a URL safe base64 encoded mongo object-id-like unique id.\n// Mongo unique id generation algorithm has been selected as a trade-off between\n// size and ease of use: UUID is less space efficient and snowflake requires machine\n// configuration.\nfunc RequestIDHandler(name, headerName string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tctx := r.Context()\n\t\t\tid, ok := IDFromContext(ctx)\n\t\t\tif !ok {\n\t\t\t\tid = xid.New()\n\t\t\t\tctx = context.WithValue(ctx, idKey, id)\n\t\t\t\tr = r.WithContext(ctx)\n\t\t\t}\n\t\t\tif name != \"\" {\n\t\t\t\tFromContext(ctx).SetField(name, id)\n\t\t\t}\n\t\t\tif headerName != \"\" {\n\t\t\t\tw.Header().Set(headerName, id.String())\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "handler_examples_test.go",
    "content": "// +build go1.7\n\npackage xlog_test\n\nimport (\n\t\"errors\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"github.com/justinas/alice\"\n\t\"github.com/rs/xlog\"\n)\n\nfunc Example_handler() {\n\tc := alice.New()\n\n\thost, _ := os.Hostname()\n\tconf := xlog.Config{\n\t\t// Set some global env fields\n\t\tFields: xlog.F{\n\t\t\t\"role\": \"my-service\",\n\t\t\t\"host\": host,\n\t\t},\n\t}\n\n\t// Install the logger handler with default output on the console\n\tc = c.Append(xlog.NewHandler(conf))\n\n\t// Plug the xlog handler's input to Go's default logger\n\tlog.SetFlags(0)\n\tlog.SetOutput(xlog.New(conf))\n\n\t// Install some provided extra handler to set some request's context fields.\n\t// Thanks to those handler, all our logs will come with some pre-populated fields.\n\tc = c.Append(xlog.RemoteAddrHandler(\"ip\"))\n\tc = c.Append(xlog.UserAgentHandler(\"user_agent\"))\n\tc = c.Append(xlog.RefererHandler(\"referer\"))\n\tc = c.Append(xlog.RequestIDHandler(\"req_id\", \"Request-Id\"))\n\n\t// Here is your final handler\n\th := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t// Get the logger from the request's context. You can safely assume it\n\t\t// will be always there: if the handler is removed, xlog.FromContext\n\t\t// will return a NopLogger\n\t\tl := xlog.FromRequest(r)\n\n\t\t// Then log some errors\n\t\tif err := errors.New(\"some error from elsewhere\"); err != nil {\n\t\t\tl.Errorf(\"Here is an error: %v\", err)\n\t\t}\n\n\t\t// Or some info with fields\n\t\tl.Info(\"Something happend\", xlog.F{\n\t\t\t\"user\":   \"current user id\",\n\t\t\t\"status\": \"ok\",\n\t\t})\n\t}))\n\thttp.Handle(\"/\", h)\n\n\tif err := http.ListenAndServe(\":8080\", nil); err != nil {\n\t\tlog.SetOutput(os.Stderr) // make sure we print to console\n\t\tlog.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "handler_pre17.go",
    "content": "// +build !go1.7\n\npackage xlog\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\n\t\"github.com/rs/xhandler\"\n\t\"github.com/rs/xid\"\n\t\"golang.org/x/net/context\"\n)\n\ntype key int\n\nconst (\n\tlogKey key = iota\n\tidKey\n)\n\n// IDFromContext returns the unique id associated to the request if any.\nfunc IDFromContext(ctx context.Context) (xid.ID, bool) {\n\tid, ok := ctx.Value(idKey).(xid.ID)\n\treturn id, ok\n}\n\n// FromContext gets the logger out of the context.\n// If not logger is stored in the context, a NopLogger is returned.\nfunc FromContext(ctx context.Context) Logger {\n\tif ctx == nil {\n\t\treturn NopLogger\n\t}\n\tl, ok := ctx.Value(logKey).(Logger)\n\tif !ok {\n\t\treturn NopLogger\n\t}\n\treturn l\n}\n\n// NewContext returns a copy of the parent context and associates it with the provided logger.\nfunc NewContext(ctx context.Context, l Logger) context.Context {\n\treturn context.WithValue(ctx, logKey, l)\n}\n\n// NewHandler instanciates a new xlog HTTP handler.\n//\n// If not configured, the output is set to NewConsoleOutput() by default.\nfunc NewHandler(c Config) func(xhandler.HandlerC) xhandler.HandlerC {\n\tif c.Output == nil {\n\t\tc.Output = NewOutputChannel(NewConsoleOutput())\n\t}\n\treturn func(next xhandler.HandlerC) xhandler.HandlerC {\n\t\treturn xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\t\tl := New(c)\n\t\t\tctx = NewContext(ctx, l)\n\t\t\tnext.ServeHTTPC(ctx, w, r)\n\t\t\tif l, ok := l.(*logger); ok {\n\t\t\t\tl.close()\n\t\t\t}\n\t\t})\n\t}\n}\n\n// URLHandler returns a handler setting the request's URL as a field\n// to the current context's logger using the passed name as field name.\nfunc URLHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {\n\treturn func(next xhandler.HandlerC) xhandler.HandlerC {\n\t\treturn xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\t\tFromContext(ctx).SetField(name, r.URL.String())\n\t\t\tnext.ServeHTTPC(ctx, w, r)\n\t\t})\n\t}\n}\n\n// MethodHandler returns a handler setting the request's method as a field\n// to the current context's logger using the passed name as field name.\nfunc MethodHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {\n\treturn func(next xhandler.HandlerC) xhandler.HandlerC {\n\t\treturn xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\t\tFromContext(ctx).SetField(name, r.Method)\n\t\t\tnext.ServeHTTPC(ctx, w, r)\n\t\t})\n\t}\n}\n\n// RequestHandler returns a handler setting the request's method and URL as a field\n// to the current context's logger using the passed name as field name.\nfunc RequestHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {\n\treturn func(next xhandler.HandlerC) xhandler.HandlerC {\n\t\treturn xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\t\tFromContext(ctx).SetField(name, r.Method+\" \"+r.URL.String())\n\t\t\tnext.ServeHTTPC(ctx, w, r)\n\t\t})\n\t}\n}\n\n// RemoteAddrHandler returns a handler setting the request's remote address as a field\n// to the current context's logger using the passed name as field name.\nfunc RemoteAddrHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {\n\treturn func(next xhandler.HandlerC) xhandler.HandlerC {\n\t\treturn xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\t\tif host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {\n\t\t\t\tFromContext(ctx).SetField(name, host)\n\t\t\t}\n\t\t\tnext.ServeHTTPC(ctx, w, r)\n\t\t})\n\t}\n}\n\n// UserAgentHandler returns a handler setting the request's client's user-agent as\n// a field to the current context's logger using the passed name as field name.\nfunc UserAgentHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {\n\treturn func(next xhandler.HandlerC) xhandler.HandlerC {\n\t\treturn xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\t\tif ua := r.Header.Get(\"User-Agent\"); ua != \"\" {\n\t\t\t\tFromContext(ctx).SetField(name, ua)\n\t\t\t}\n\t\t\tnext.ServeHTTPC(ctx, w, r)\n\t\t})\n\t}\n}\n\n// RefererHandler returns a handler setting the request's referer header as\n// a field to the current context's logger using the passed name as field name.\nfunc RefererHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {\n\treturn func(next xhandler.HandlerC) xhandler.HandlerC {\n\t\treturn xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\t\tif ref := r.Header.Get(\"Referer\"); ref != \"\" {\n\t\t\t\tFromContext(ctx).SetField(name, ref)\n\t\t\t}\n\t\t\tnext.ServeHTTPC(ctx, w, r)\n\t\t})\n\t}\n}\n\n// RequestIDHandler returns a handler setting a unique id to the request which can\n// be gathered using IDFromContext(ctx). This generated id is added as a field to the\n// logger using the passed name as field name. The id is also added as a response\n// header if the headerName is not empty.\n//\n// The generated id is a URL safe base64 encoded mongo object-id-like unique id.\n// Mongo unique id generation algorithm has been selected as a trade-off between\n// size and ease of use: UUID is less space efficient and snowflake requires machine\n// configuration.\nfunc RequestIDHandler(name, headerName string) func(next xhandler.HandlerC) xhandler.HandlerC {\n\treturn func(next xhandler.HandlerC) xhandler.HandlerC {\n\t\treturn xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\t\tid, ok := IDFromContext(ctx)\n\t\t\tif !ok {\n\t\t\t\tid = xid.New()\n\t\t\t\tctx = context.WithValue(ctx, idKey, id)\n\t\t\t}\n\t\t\tif name != \"\" {\n\t\t\t\tFromContext(ctx).SetField(name, id)\n\t\t\t}\n\t\t\tif headerName != \"\" {\n\t\t\t\tw.Header().Set(headerName, id.String())\n\t\t\t}\n\t\t\tnext.ServeHTTPC(ctx, w, r)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "handler_pre17_test.go",
    "content": "// +build !go1.7\n\npackage xlog\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/rs/xhandler\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"golang.org/x/net/context\"\n)\n\nfunc TestFromContext(t *testing.T) {\n\tassert.Equal(t, NopLogger, FromContext(nil))\n\tassert.Equal(t, NopLogger, FromContext(context.Background()))\n\tl := &logger{}\n\tctx := NewContext(context.Background(), l)\n\tassert.Equal(t, l, FromContext(ctx))\n}\n\nfunc TestNewHandler(t *testing.T) {\n\tc := Config{\n\t\tLevel:  LevelInfo,\n\t\tFields: F{\"foo\": \"bar\"},\n\t\tOutput: NewOutputChannel(&testOutput{}),\n\t}\n\tlh := NewHandler(c)\n\th := lh(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\tl := FromContext(ctx)\n\t\tassert.NotNil(t, l)\n\t\tassert.NotEqual(t, NopLogger, l)\n\t\tif l, ok := l.(*logger); assert.True(t, ok) {\n\t\t\tassert.Equal(t, LevelInfo, l.level)\n\t\t\tassert.Equal(t, c.Output, l.output)\n\t\t\tassert.Equal(t, F{\"foo\": \"bar\"}, F(l.fields))\n\t\t}\n\t}))\n\th.ServeHTTPC(context.Background(), nil, nil)\n}\n\nfunc TestURLHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tURL: &url.URL{Path: \"/path\", RawQuery: \"foo=bar\"},\n\t}\n\th := URLHandler(\"url\")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\tl := FromContext(ctx).(*logger)\n\t\tassert.Equal(t, F{\"url\": \"/path?foo=bar\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTPC(context.Background(), nil, r)\n}\n\nfunc TestMethodHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tMethod: \"POST\",\n\t}\n\th := MethodHandler(\"method\")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\tl := FromContext(ctx).(*logger)\n\t\tassert.Equal(t, F{\"method\": \"POST\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTPC(context.Background(), nil, r)\n}\n\nfunc TestRequestHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tMethod: \"POST\",\n\t\tURL:    &url.URL{Path: \"/path\", RawQuery: \"foo=bar\"},\n\t}\n\th := RequestHandler(\"request\")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\tl := FromContext(ctx).(*logger)\n\t\tassert.Equal(t, F{\"request\": \"POST /path?foo=bar\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTPC(context.Background(), nil, r)\n}\n\nfunc TestRemoteAddrHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tRemoteAddr: \"1.2.3.4:1234\",\n\t}\n\th := RemoteAddrHandler(\"ip\")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\tl := FromContext(ctx).(*logger)\n\t\tassert.Equal(t, F{\"ip\": \"1.2.3.4\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTPC(context.Background(), nil, r)\n}\n\nfunc TestRemoteAddrHandlerIPv6(t *testing.T) {\n\tr := &http.Request{\n\t\tRemoteAddr: \"[2001:db8:a0b:12f0::1]:1234\",\n\t}\n\th := RemoteAddrHandler(\"ip\")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\tl := FromContext(ctx).(*logger)\n\t\tassert.Equal(t, F{\"ip\": \"2001:db8:a0b:12f0::1\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTPC(context.Background(), nil, r)\n}\n\nfunc TestUserAgentHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tHeader: http.Header{\n\t\t\t\"User-Agent\": []string{\"some user agent string\"},\n\t\t},\n\t}\n\th := UserAgentHandler(\"ua\")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\tl := FromContext(ctx).(*logger)\n\t\tassert.Equal(t, F{\"ua\": \"some user agent string\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTPC(context.Background(), nil, r)\n}\n\nfunc TestRefererHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tHeader: http.Header{\n\t\t\t\"Referer\": []string{\"http://foo.com/bar\"},\n\t\t},\n\t}\n\th := RefererHandler(\"ua\")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\tl := FromContext(ctx).(*logger)\n\t\tassert.Equal(t, F{\"ua\": \"http://foo.com/bar\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTPC(context.Background(), nil, r)\n}\n\nfunc TestRequestIDHandler(t *testing.T) {\n\tr := &http.Request{}\n\th := RequestIDHandler(\"id\", \"Request-Id\")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {\n\t\tl := FromContext(ctx).(*logger)\n\t\tif id, ok := IDFromContext(ctx); assert.True(t, ok) {\n\t\t\tassert.Equal(t, l.fields[\"id\"], id)\n\t\t\tassert.Len(t, id.String(), 20)\n\t\t\tassert.Equal(t, id.String(), w.Header().Get(\"Request-Id\"))\n\t\t}\n\t\tassert.Len(t, l.fields[\"id\"], 12)\n\t}))\n\th = NewHandler(Config{})(h)\n\tw := httptest.NewRecorder()\n\th.ServeHTTPC(context.Background(), w, r)\n}\n"
  },
  {
    "path": "handler_test.go",
    "content": "// +build go1.7\n\npackage xlog\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestFromContext(t *testing.T) {\n\tassert.Equal(t, NopLogger, FromContext(nil))\n\tassert.Equal(t, NopLogger, FromContext(context.Background()))\n\tl := &logger{}\n\tctx := NewContext(context.Background(), l)\n\tassert.Equal(t, l, FromContext(ctx))\n}\n\nfunc TestNewHandler(t *testing.T) {\n\tc := Config{\n\t\tLevel:  LevelInfo,\n\t\tFields: F{\"foo\": \"bar\"},\n\t\tOutput: NewOutputChannel(&testOutput{}),\n\t}\n\tlh := NewHandler(c)\n\th := lh(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tassert.NotNil(t, l)\n\t\tassert.NotEqual(t, NopLogger, l)\n\t\tif l, ok := l.(*logger); assert.True(t, ok) {\n\t\t\tassert.Equal(t, LevelInfo, l.level)\n\t\t\tassert.Equal(t, c.Output, l.output)\n\t\t\tassert.Equal(t, F{\"foo\": \"bar\"}, F(l.fields))\n\t\t}\n\t}))\n\th.ServeHTTP(nil, &http.Request{})\n}\n\nfunc TestURLHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tURL: &url.URL{Path: \"/path\", RawQuery: \"foo=bar\"},\n\t}\n\th := URLHandler(\"url\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r).(*logger)\n\t\tassert.Equal(t, F{\"url\": \"/path?foo=bar\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTP(nil, r)\n}\n\nfunc TestMethodHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tMethod: \"POST\",\n\t}\n\th := MethodHandler(\"method\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r).(*logger)\n\t\tassert.Equal(t, F{\"method\": \"POST\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTP(nil, r)\n}\n\nfunc TestRequestHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tMethod: \"POST\",\n\t\tURL:    &url.URL{Path: \"/path\", RawQuery: \"foo=bar\"},\n\t}\n\th := RequestHandler(\"request\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r).(*logger)\n\t\tassert.Equal(t, F{\"request\": \"POST /path?foo=bar\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTP(nil, r)\n}\n\nfunc TestRemoteAddrHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tRemoteAddr: \"1.2.3.4:1234\",\n\t}\n\th := RemoteAddrHandler(\"ip\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r).(*logger)\n\t\tassert.Equal(t, F{\"ip\": \"1.2.3.4\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTP(nil, r)\n}\n\nfunc TestRemoteAddrHandlerIPv6(t *testing.T) {\n\tr := &http.Request{\n\t\tRemoteAddr: \"[2001:db8:a0b:12f0::1]:1234\",\n\t}\n\th := RemoteAddrHandler(\"ip\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r).(*logger)\n\t\tassert.Equal(t, F{\"ip\": \"2001:db8:a0b:12f0::1\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTP(nil, r)\n}\n\nfunc TestUserAgentHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tHeader: http.Header{\n\t\t\t\"User-Agent\": []string{\"some user agent string\"},\n\t\t},\n\t}\n\th := UserAgentHandler(\"ua\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r).(*logger)\n\t\tassert.Equal(t, F{\"ua\": \"some user agent string\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTP(nil, r)\n}\n\nfunc TestRefererHandler(t *testing.T) {\n\tr := &http.Request{\n\t\tHeader: http.Header{\n\t\t\t\"Referer\": []string{\"http://foo.com/bar\"},\n\t\t},\n\t}\n\th := RefererHandler(\"ua\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r).(*logger)\n\t\tassert.Equal(t, F{\"ua\": \"http://foo.com/bar\"}, F(l.fields))\n\t}))\n\th = NewHandler(Config{})(h)\n\th.ServeHTTP(nil, r)\n}\n\nfunc TestRequestIDHandler(t *testing.T) {\n\tr := &http.Request{}\n\th := RequestIDHandler(\"id\", \"Request-Id\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r).(*logger)\n\t\tif id, ok := IDFromRequest(r); assert.True(t, ok) {\n\t\t\tassert.Equal(t, l.fields[\"id\"], id)\n\t\t\tassert.Len(t, id.String(), 20)\n\t\t\tassert.Equal(t, id.String(), w.Header().Get(\"Request-Id\"))\n\t\t}\n\t\tassert.Len(t, l.fields[\"id\"], 12)\n\t}))\n\th = NewHandler(Config{})(h)\n\tw := httptest.NewRecorder()\n\th.ServeHTTP(w, r)\n}\n"
  },
  {
    "path": "internal/term/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Simon Eskildsen\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "internal/term/term.go",
    "content": "// Based on ssh/terminal:\n// Copyright 2011 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build linux,!appengine darwin freebsd openbsd\n\npackage term\n\ntype fder interface {\n\tFd() uintptr\n}\n"
  },
  {
    "path": "internal/term/term_appengine.go",
    "content": "// Based on ssh/terminal:\n// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build appengine\n\npackage term\n\nimport \"io\"\n\n// IsTerminal always returns false on AppEngine.\nfunc IsTerminal(w io.Writer) bool {\n\treturn false\n}\n"
  },
  {
    "path": "internal/term/term_darwin.go",
    "content": "// Based on ssh/terminal:\n// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage term\n\nimport \"syscall\"\n\nconst ioctlReadTermios = syscall.TIOCGETA\n"
  },
  {
    "path": "internal/term/term_freebsd.go",
    "content": "package term\n\nimport (\n\t\"syscall\"\n)\n\nconst ioctlReadTermios = syscall.TIOCGETA\n"
  },
  {
    "path": "internal/term/term_linux.go",
    "content": "// Based on ssh/terminal:\n// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build !appengine\n\npackage term\n\nimport \"syscall\"\n\nconst ioctlReadTermios = syscall.TCGETS\n"
  },
  {
    "path": "internal/term/term_notwindows.go",
    "content": "// Based on ssh/terminal:\n// Copyright 2011 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build linux,!appengine darwin freebsd openbsd\n\npackage term\n\nimport (\n\t\"io\"\n\t\"syscall\"\n\t\"unsafe\"\n)\n\n// IsTerminal returns true if w writes to a terminal.\nfunc IsTerminal(w io.Writer) bool {\n\tfw, ok := w.(fder)\n\tif !ok {\n\t\treturn false\n\t}\n\tvar termios syscall.Termios\n\t_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fw.Fd(), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)\n\treturn err == 0\n}\n"
  },
  {
    "path": "internal/term/term_openbsd.go",
    "content": "package term\r\n\r\nimport \"syscall\"\r\n\r\nconst ioctlReadTermios = syscall.TIOCGETA\r\n"
  },
  {
    "path": "internal/term/term_windows.go",
    "content": "// Based on ssh/terminal:\n// Copyright 2011 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// +build windows\n\npackage term\n\nimport (\n\t\"io\"\n\t\"syscall\"\n\t\"unsafe\"\n)\n\nvar kernel32 = syscall.NewLazyDLL(\"kernel32.dll\")\n\nvar (\n\tprocGetConsoleMode = kernel32.NewProc(\"GetConsoleMode\")\n)\n\n// IsTerminal returns true if w writes to a terminal.\nfunc IsTerminal(w io.Writer) bool {\n\tfw, ok := w.(interface {\n\t\tFd() uintptr\n\t})\n\tif !ok {\n\t\treturn false\n\t}\n\tvar st uint32\n\tr, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fw.Fd(), uintptr(unsafe.Pointer(&st)), 0)\n\treturn r != 0 && e == 0\n}\n"
  },
  {
    "path": "levels.go",
    "content": "package xlog\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strconv\"\n)\n\n// Level defines log levels\ntype Level int\n\n// Log levels\nconst (\n\tLevelDebug Level = iota\n\tLevelInfo\n\tLevelWarn\n\tLevelError\n\tLevelFatal\n)\n\n// Log level strings\nvar (\n\tlevelDebug = \"debug\"\n\tlevelInfo  = \"info\"\n\tlevelWarn  = \"warn\"\n\tlevelError = \"error\"\n\tlevelFatal = \"fatal\"\n\n\tlevelBytesDebug = []byte(levelDebug)\n\tlevelBytesInfo  = []byte(levelInfo)\n\tlevelBytesWarn  = []byte(levelWarn)\n\tlevelBytesError = []byte(levelError)\n\tlevelBytesFatal = []byte(levelFatal)\n)\n\n// LevelFromString returns the level based on its string representation\nfunc LevelFromString(t string) (Level, error) {\n\tl := Level(0)\n\terr := (&l).UnmarshalText([]byte(t))\n\treturn l, err\n}\n\n// UnmarshalText lets Level implements the TextUnmarshaler interface used by encoding packages\nfunc (l *Level) UnmarshalText(text []byte) (err error) {\n\tif bytes.Equal(text, levelBytesDebug) {\n\t\t*l = LevelDebug\n\t} else if bytes.Equal(text, levelBytesInfo) {\n\t\t*l = LevelInfo\n\t} else if bytes.Equal(text, levelBytesWarn) {\n\t\t*l = LevelWarn\n\t} else if bytes.Equal(text, levelBytesError) {\n\t\t*l = LevelError\n\t} else if bytes.Equal(text, levelBytesFatal) {\n\t\t*l = LevelFatal\n\t} else {\n\t\terr = fmt.Errorf(\"Uknown level %v\", string(text))\n\t}\n\treturn\n}\n\n// String returns the string representation of the level.\nfunc (l Level) String() string {\n\tvar t string\n\tswitch l {\n\tcase LevelDebug:\n\t\tt = levelDebug\n\tcase LevelInfo:\n\t\tt = levelInfo\n\tcase LevelWarn:\n\t\tt = levelWarn\n\tcase LevelError:\n\t\tt = levelError\n\tcase LevelFatal:\n\t\tt = levelFatal\n\tdefault:\n\t\tt = strconv.FormatInt(int64(l), 10)\n\t}\n\treturn t\n}\n\n// MarshalText lets Level implements the TextMarshaler interface used by encoding packages\nfunc (l Level) MarshalText() ([]byte, error) {\n\tvar t []byte\n\tswitch l {\n\tcase LevelDebug:\n\t\tt = levelBytesDebug\n\tcase LevelInfo:\n\t\tt = levelBytesInfo\n\tcase LevelWarn:\n\t\tt = levelBytesWarn\n\tcase LevelError:\n\t\tt = levelBytesError\n\tcase LevelFatal:\n\t\tt = levelBytesFatal\n\tdefault:\n\t\tt = []byte(strconv.FormatInt(int64(l), 10))\n\t}\n\treturn t, nil\n}\n"
  },
  {
    "path": "levels_test.go",
    "content": "package xlog\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLevelFromString(t *testing.T) {\n\tl, err := LevelFromString(\"debug\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, LevelDebug, l)\n\tl, err = LevelFromString(\"info\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, LevelInfo, l)\n\tl, err = LevelFromString(\"warn\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, LevelWarn, l)\n\tl, err = LevelFromString(\"error\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, LevelError, l)\n\tl, err = LevelFromString(\"fatal\")\n\tassert.NoError(t, err)\n\tassert.Equal(t, LevelFatal, l)\n\t_, err = LevelFromString(\"foo\")\n\tassert.Error(t, err, \"\")\n}\n\nfunc TestLevelUnmarshalerText(t *testing.T) {\n\tl := Level(-1)\n\terr := l.UnmarshalText([]byte(\"debug\"))\n\tassert.NoError(t, err)\n\tassert.Equal(t, LevelDebug, l)\n\terr = l.UnmarshalText([]byte(\"info\"))\n\tassert.NoError(t, err)\n\tassert.Equal(t, LevelInfo, l)\n\terr = l.UnmarshalText([]byte(\"warn\"))\n\tassert.NoError(t, err)\n\tassert.Equal(t, LevelWarn, l)\n\terr = l.UnmarshalText([]byte(\"error\"))\n\tassert.NoError(t, err)\n\tassert.Equal(t, LevelError, l)\n\terr = l.UnmarshalText([]byte(\"fatal\"))\n\tassert.NoError(t, err)\n\tassert.Equal(t, LevelFatal, l)\n\tassert.Error(t, l.UnmarshalText([]byte(\"invalid\")))\n}\n\nfunc TestLevelString(t *testing.T) {\n\tassert.Equal(t, \"debug\", LevelDebug.String())\n\tassert.Equal(t, \"info\", LevelInfo.String())\n\tassert.Equal(t, \"warn\", LevelWarn.String())\n\tassert.Equal(t, \"error\", LevelError.String())\n\tassert.Equal(t, \"fatal\", LevelFatal.String())\n\tassert.Equal(t, \"10\", Level(10).String())\n}\n\nfunc TestLevelMarshalerText(t *testing.T) {\n\tb, err := LevelDebug.MarshalText()\n\tassert.NoError(t, err)\n\tassert.Equal(t, string(levelBytesDebug), string(b))\n\tb, err = LevelInfo.MarshalText()\n\tassert.NoError(t, err)\n\tassert.Equal(t, string(levelBytesInfo), string(b))\n\tb, err = LevelWarn.MarshalText()\n\tassert.NoError(t, err)\n\tassert.Equal(t, string(levelBytesWarn), string(b))\n\tb, err = LevelError.MarshalText()\n\tassert.NoError(t, err)\n\tassert.Equal(t, string(levelBytesError), string(b))\n\tb, err = LevelFatal.MarshalText()\n\tassert.NoError(t, err)\n\tassert.Equal(t, string(levelBytesFatal), string(b))\n\tb, err = Level(10).MarshalText()\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"10\", string(b))\n}\n"
  },
  {
    "path": "nop.go",
    "content": "package xlog\n\ntype nop struct{}\n\n// NopLogger is an no-op implementation of xlog.Logger\nvar NopLogger = &nop{}\n\nfunc (n nop) SetField(name string, value interface{}) {}\n\nfunc (n nop) GetFields() F { return map[string]interface{}{} }\n\nfunc (n nop) OutputF(level Level, calldepth int, msg string, fields map[string]interface{}) {}\n\nfunc (n nop) Debug(v ...interface{}) {}\n\nfunc (n nop) Debugf(format string, v ...interface{}) {}\n\nfunc (n nop) Info(v ...interface{}) {}\n\nfunc (n nop) Infof(format string, v ...interface{}) {}\n\nfunc (n nop) Warn(v ...interface{}) {}\n\nfunc (n nop) Warnf(format string, v ...interface{}) {}\n\nfunc (n nop) Error(v ...interface{}) {}\n\nfunc (n nop) Errorf(format string, v ...interface{}) {}\n\nfunc (n nop) Fatal(v ...interface{}) {\n\texit1()\n}\n\nfunc (n nop) Fatalf(format string, v ...interface{}) {\n\texit1()\n}\n\nfunc (n nop) Write(p []byte) (int, error) { return len(p), nil }\n\nfunc (n nop) Output(calldepth int, s string) error {\n\treturn nil\n}\n"
  },
  {
    "path": "nop_test.go",
    "content": "package xlog\n\nimport \"testing\"\n\nfunc TestNopLogger(t *testing.T) {\n\t// cheap cover score upper\n\tNopLogger.SetField(\"name\", \"value\")\n\tNopLogger.OutputF(LevelInfo, 0, \"\", nil)\n\tNopLogger.Debug()\n\tNopLogger.Debugf(\"format\")\n\tNopLogger.Info()\n\tNopLogger.Infof(\"format\")\n\tNopLogger.Warn()\n\tNopLogger.Warnf(\"format\")\n\tNopLogger.Error()\n\tNopLogger.Errorf(\"format\")\n\texit1 = func() {}\n\tNopLogger.Fatal()\n\tNopLogger.Fatalf(\"format\")\n\tNopLogger.Write([]byte{})\n\tNopLogger.Output(0, \"\")\n}\n"
  },
  {
    "path": "output.go",
    "content": "package xlog\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rs/xid\"\n\t\"github.com/rs/xlog/internal/term\"\n)\n\n// Output sends a log message fields to a destination.\ntype Output interface {\n\tWrite(fields map[string]interface{}) error\n}\n\n// OutputFunc is an adapter to allow the use of ordinary functions as Output handlers.\n// If it is a function with the appropriate signature, OutputFunc(f) is a Output object\n// that calls f on Write().\ntype OutputFunc func(fields map[string]interface{}) error\n\nfunc (of OutputFunc) Write(fields map[string]interface{}) error {\n\treturn of(fields)\n}\n\n// OutputChannel is a send buffered channel between xlog and an Output.\ntype OutputChannel struct {\n\tinput  chan map[string]interface{}\n\toutput Output\n\tstop   chan struct{}\n}\n\n// ErrBufferFull is returned when the output channel buffer is full and messages\n// are discarded.\nvar ErrBufferFull = errors.New(\"buffer full\")\n\n// NewOutputChannel creates a consumer buffered channel for the given output\n// with a default buffer of 100 messages.\nfunc NewOutputChannel(o Output) *OutputChannel {\n\treturn NewOutputChannelBuffer(o, 100)\n}\n\n// NewOutputChannelBuffer creates a consumer buffered channel for the given output\n// with a customizable buffer size.\nfunc NewOutputChannelBuffer(o Output, bufSize int) *OutputChannel {\n\toc := &OutputChannel{\n\t\tinput:  make(chan map[string]interface{}, bufSize),\n\t\toutput: o,\n\t\tstop:   make(chan struct{}),\n\t}\n\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase msg := <-oc.input:\n\t\t\t\tif err := o.Write(msg); err != nil {\n\t\t\t\t\tcritialLogger.Print(\"cannot write log message: \", err.Error())\n\t\t\t\t}\n\t\t\tcase <-oc.stop:\n\t\t\t\tclose(oc.stop)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn oc\n}\n\n// Write implements the Output interface\nfunc (oc *OutputChannel) Write(fields map[string]interface{}) (err error) {\n\tselect {\n\tcase oc.input <- fields:\n\t\t// Sent with success\n\tdefault:\n\t\t// Channel is full, message dropped\n\t\terr = ErrBufferFull\n\t}\n\treturn err\n}\n\n// Flush flushes all the buffered message to the output\nfunc (oc *OutputChannel) Flush() {\n\tfor {\n\t\tselect {\n\t\tcase msg := <-oc.input:\n\t\t\tif err := oc.output.Write(msg); err != nil {\n\t\t\t\tcritialLogger.Print(\"cannot write log message: \", err.Error())\n\t\t\t}\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Close closes the output channel and release the consumer's go routine.\nfunc (oc *OutputChannel) Close() {\n\tif oc.stop == nil {\n\t\treturn\n\t}\n\toc.stop <- struct{}{}\n\t<-oc.stop\n\toc.stop = nil\n\toc.Flush()\n}\n\n// Discard is an Output that discards all log message going thru it.\nvar Discard = OutputFunc(func(fields map[string]interface{}) error {\n\treturn nil\n})\n\nvar bufPool = &sync.Pool{\n\tNew: func() interface{} {\n\t\treturn &bytes.Buffer{}\n\t},\n}\n\n// MultiOutput routes the same message to serveral outputs.\n// If one or more outputs return an error, the last error is returned.\ntype MultiOutput []Output\n\nfunc (m MultiOutput) Write(fields map[string]interface{}) (err error) {\n\tfor _, o := range m {\n\t\te := o.Write(fields)\n\t\tif e != nil {\n\t\t\terr = e\n\t\t}\n\t}\n\treturn\n}\n\n// FilterOutput test a condition on the message and forward it to the child output\n// if it returns true.\ntype FilterOutput struct {\n\tCond   func(fields map[string]interface{}) bool\n\tOutput Output\n}\n\nfunc (f FilterOutput) Write(fields map[string]interface{}) (err error) {\n\tif f.Output == nil {\n\t\treturn\n\t}\n\tif f.Cond(fields) {\n\t\treturn f.Output.Write(fields)\n\t}\n\treturn\n}\n\n// LevelOutput routes messages to different output based on the message's level.\ntype LevelOutput struct {\n\tDebug Output\n\tInfo  Output\n\tWarn  Output\n\tError Output\n\tFatal Output\n}\n\nfunc (l LevelOutput) Write(fields map[string]interface{}) error {\n\tvar o Output\n\tswitch fields[KeyLevel] {\n\tcase \"debug\":\n\t\to = l.Debug\n\tcase \"info\":\n\t\to = l.Info\n\tcase \"warn\":\n\t\to = l.Warn\n\tcase \"error\":\n\t\to = l.Error\n\tcase \"fatal\":\n\t\to = l.Fatal\n\t}\n\tif o != nil {\n\t\treturn o.Write(fields)\n\t}\n\treturn nil\n}\n\n// RecorderOutput stores the raw messages in it's Messages field. This output is useful for testing.\ntype RecorderOutput struct {\n\tMessages []F\n}\n\nfunc (l *RecorderOutput) Write(fields map[string]interface{}) error {\n\tif l.Messages == nil {\n\t\tl.Messages = []F{fields}\n\t} else {\n\t\tl.Messages = append(l.Messages, fields)\n\t}\n\treturn nil\n}\n\n// Reset empty the output from stored messages\nfunc (l *RecorderOutput) Reset() {\n\tl.Messages = []F{}\n}\n\ntype consoleOutput struct {\n\tw io.Writer\n}\n\nvar isTerminal = term.IsTerminal\n\n// NewConsoleOutput returns a Output printing message in a colored human readable form on the\n// stderr. If the stderr is not on a terminal, a LogfmtOutput is returned instead.\nfunc NewConsoleOutput() Output {\n\treturn NewConsoleOutputW(os.Stderr, NewLogfmtOutput(os.Stderr))\n}\n\n// NewConsoleOutputW returns a Output printing message in a colored human readable form with\n// the provided writer. If the writer is not on a terminal, the noTerm output is returned.\nfunc NewConsoleOutputW(w io.Writer, noTerm Output) Output {\n\tif isTerminal(w) {\n\t\treturn consoleOutput{w: w}\n\t}\n\treturn noTerm\n}\n\nfunc (o consoleOutput) Write(fields map[string]interface{}) error {\n\tbuf := bufPool.Get().(*bytes.Buffer)\n\tdefer func() {\n\t\tbuf.Reset()\n\t\tbufPool.Put(buf)\n\t}()\n\tif ts, ok := fields[KeyTime].(time.Time); ok {\n\t\tbuf.Write([]byte(ts.Format(\"2006/01/02 15:04:05 \")))\n\t}\n\tif lvl, ok := fields[KeyLevel].(string); ok {\n\t\tlevelColor := blue\n\t\tswitch lvl {\n\t\tcase \"debug\":\n\t\t\tlevelColor = gray\n\t\tcase \"warn\":\n\t\t\tlevelColor = yellow\n\t\tcase \"error\":\n\t\t\tlevelColor = red\n\t\t}\n\t\tcolorPrint(buf, strings.ToUpper(lvl[0:4]), levelColor)\n\t\tbuf.WriteByte(' ')\n\t}\n\tif msg, ok := fields[KeyMessage].(string); ok {\n\t\tmsg = strings.Replace(msg, \"\\n\", \"\\\\n\", -1)\n\t\tbuf.Write([]byte(msg))\n\t}\n\t// Gather field keys\n\tkeys := []string{}\n\tfor k := range fields {\n\t\tswitch k {\n\t\tcase KeyLevel, KeyMessage, KeyTime:\n\t\t\tcontinue\n\t\t}\n\t\tkeys = append(keys, k)\n\t}\n\t// Sort fields by key names\n\tsort.Strings(keys)\n\t// Print fields using logfmt format\n\tfor _, k := range keys {\n\t\tbuf.WriteByte(' ')\n\t\tcolorPrint(buf, k, green)\n\t\tbuf.WriteByte('=')\n\t\tif err := writeValue(buf, fields[k]); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tbuf.WriteByte('\\n')\n\t_, err := o.w.Write(buf.Bytes())\n\treturn err\n}\n\ntype logfmtOutput struct {\n\tw io.Writer\n}\n\n// NewLogfmtOutput returns a new output using logstash JSON schema v1\nfunc NewLogfmtOutput(w io.Writer) Output {\n\treturn logfmtOutput{w: w}\n}\n\nfunc (o logfmtOutput) Write(fields map[string]interface{}) error {\n\tbuf := bufPool.Get().(*bytes.Buffer)\n\tdefer func() {\n\t\tbuf.Reset()\n\t\tbufPool.Put(buf)\n\t}()\n\t// Gather field keys\n\tkeys := []string{}\n\tfor k := range fields {\n\t\tswitch k {\n\t\tcase KeyLevel, KeyMessage, KeyTime:\n\t\t\tcontinue\n\t\t}\n\t\tkeys = append(keys, k)\n\t}\n\t// Sort fields by key names\n\tsort.Strings(keys)\n\t// Prepend default fields in a specific order\n\tkeys = append([]string{KeyLevel, KeyMessage, KeyTime}, keys...)\n\tl := len(keys)\n\tfor i, k := range keys {\n\t\tbuf.Write([]byte(k))\n\t\tbuf.WriteByte('=')\n\t\tif err := writeValue(buf, fields[k]); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif i+1 < l {\n\t\t\tbuf.WriteByte(' ')\n\t\t} else {\n\t\t\tbuf.WriteByte('\\n')\n\t\t}\n\t}\n\t_, err := o.w.Write(buf.Bytes())\n\treturn err\n}\n\n// NewJSONOutput returns a new JSON output with the given writer.\nfunc NewJSONOutput(w io.Writer) Output {\n\tenc := json.NewEncoder(w)\n\treturn OutputFunc(func(fields map[string]interface{}) error {\n\t\treturn enc.Encode(fields)\n\t})\n}\n\n// NewLogstashOutput returns an output to generate logstash friendly JSON format.\nfunc NewLogstashOutput(w io.Writer) Output {\n\treturn OutputFunc(func(fields map[string]interface{}) error {\n\t\tlsf := map[string]interface{}{\n\t\t\t\"@version\": 1,\n\t\t}\n\t\tfor k, v := range fields {\n\t\t\tswitch k {\n\t\t\tcase KeyTime:\n\t\t\t\tk = \"@timestamp\"\n\t\t\tcase KeyLevel:\n\t\t\t\tif s, ok := v.(string); ok {\n\t\t\t\t\tv = strings.ToUpper(s)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif t, ok := v.(time.Time); ok {\n\t\t\t\tlsf[k] = t.Format(time.RFC3339)\n\t\t\t} else {\n\t\t\t\tlsf[k] = v\n\t\t\t}\n\t\t}\n\t\tb, err := json.Marshal(lsf)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = w.Write(b)\n\t\treturn err\n\t})\n}\n\n// NewUIDOutput returns an output filter adding a globally unique id (using github.com/rs/xid)\n// to all message going thru this output. The o parameter defines the next output to pass data\n// to.\nfunc NewUIDOutput(field string, o Output) Output {\n\treturn OutputFunc(func(fields map[string]interface{}) error {\n\t\tfields[field] = xid.New().String()\n\t\treturn o.Write(fields)\n\t})\n}\n\n// NewTrimOutput trims any field of type string with a value length greater than maxLen\n// to maxLen.\nfunc NewTrimOutput(maxLen int, o Output) Output {\n\treturn OutputFunc(func(fields map[string]interface{}) error {\n\t\tfor k, v := range fields {\n\t\t\tif s, ok := v.(string); ok && len(s) > maxLen {\n\t\t\t\tfields[k] = s[:maxLen]\n\t\t\t}\n\t\t}\n\t\treturn o.Write(fields)\n\t})\n}\n\n// NewTrimFieldsOutput trims listed field fields of type string with a value length greater than maxLen\n// to maxLen.\nfunc NewTrimFieldsOutput(trimFields []string, maxLen int, o Output) Output {\n\treturn OutputFunc(func(fields map[string]interface{}) error {\n\t\tfor _, f := range trimFields {\n\t\t\tif s, ok := fields[f].(string); ok && len(s) > maxLen {\n\t\t\t\tfields[f] = s[:maxLen]\n\t\t\t}\n\t\t}\n\t\treturn o.Write(fields)\n\t})\n}\n"
  },
  {
    "path": "output_examples_test.go",
    "content": "package xlog_test\n\nimport (\n\t\"log/syslog\"\n\n\t\"github.com/rs/xlog\"\n)\n\nfunc Example_combinedOutputs() {\n\tconf := xlog.Config{\n\t\tOutput: xlog.NewOutputChannel(xlog.MultiOutput{\n\t\t\t// Output interesting messages to console\n\t\t\t0: xlog.FilterOutput{\n\t\t\t\tCond: func(fields map[string]interface{}) bool {\n\t\t\t\t\tval, found := fields[\"type\"]\n\t\t\t\t\treturn found && val == \"interesting\"\n\t\t\t\t},\n\t\t\t\tOutput: xlog.NewConsoleOutput(),\n\t\t\t},\n\t\t\t// Also setup by-level loggers\n\t\t\t1: xlog.LevelOutput{\n\t\t\t\t// Send debug messages to console if they match type\n\t\t\t\tDebug: xlog.FilterOutput{\n\t\t\t\t\tCond: func(fields map[string]interface{}) bool {\n\t\t\t\t\t\tval, found := fields[\"type\"]\n\t\t\t\t\t\treturn found && val == \"interesting\"\n\t\t\t\t\t},\n\t\t\t\t\tOutput: xlog.NewConsoleOutput(),\n\t\t\t\t},\n\t\t\t},\n\t\t\t// Also send everything over syslog\n\t\t\t2: xlog.NewSyslogOutput(\"\", \"\", \"\"),\n\t\t}),\n\t}\n\n\tlh := xlog.NewHandler(conf)\n\t_ = lh\n}\n\nfunc ExampleMultiOutput() {\n\tconf := xlog.Config{\n\t\tOutput: xlog.NewOutputChannel(xlog.MultiOutput{\n\t\t\t// Output everything to console\n\t\t\t0: xlog.NewConsoleOutput(),\n\t\t\t// and also to local syslog\n\t\t\t1: xlog.NewSyslogOutput(\"\", \"\", \"\"),\n\t\t}),\n\t}\n\tlh := xlog.NewHandler(conf)\n\t_ = lh\n}\n\nfunc ExampleFilterOutput() {\n\tconf := xlog.Config{\n\t\tOutput: xlog.NewOutputChannel(xlog.FilterOutput{\n\t\t\t// Match messages containing a field type = interesting\n\t\t\tCond: func(fields map[string]interface{}) bool {\n\t\t\t\tval, found := fields[\"type\"]\n\t\t\t\treturn found && val == \"interesting\"\n\t\t\t},\n\t\t\t// Output matching messages to the console\n\t\t\tOutput: xlog.NewConsoleOutput(),\n\t\t}),\n\t}\n\n\tlh := xlog.NewHandler(conf)\n\t_ = lh\n}\n\nfunc ExampleLevelOutput() {\n\tconf := xlog.Config{\n\t\tOutput: xlog.NewOutputChannel(xlog.LevelOutput{\n\t\t\t// Send debug message to console\n\t\t\tDebug: xlog.NewConsoleOutput(),\n\t\t\t// and error messages to syslog\n\t\t\tError: xlog.NewSyslogOutput(\"\", \"\", \"\"),\n\t\t\t// other levels are discarded\n\t\t}),\n\t}\n\n\tlh := xlog.NewHandler(conf)\n\t_ = lh\n}\n\nfunc ExampleNewSyslogWriter() {\n\tconf := xlog.Config{\n\t\tOutput: xlog.NewOutputChannel(xlog.LevelOutput{\n\t\t\tDebug: xlog.NewLogstashOutput(xlog.NewSyslogWriter(\"\", \"\", syslog.LOG_LOCAL0|syslog.LOG_DEBUG, \"\")),\n\t\t\tInfo:  xlog.NewLogstashOutput(xlog.NewSyslogWriter(\"\", \"\", syslog.LOG_LOCAL0|syslog.LOG_INFO, \"\")),\n\t\t\tWarn:  xlog.NewLogstashOutput(xlog.NewSyslogWriter(\"\", \"\", syslog.LOG_LOCAL0|syslog.LOG_WARNING, \"\")),\n\t\t\tError: xlog.NewLogstashOutput(xlog.NewSyslogWriter(\"\", \"\", syslog.LOG_LOCAL0|syslog.LOG_ERR, \"\")),\n\t\t}),\n\t}\n\n\tlh := xlog.NewHandler(conf)\n\t_ = lh\n}\n"
  },
  {
    "path": "output_syslog.go",
    "content": "// +build !windows\n\npackage xlog\n\nimport (\n\t\"io\"\n\t\"log/syslog\"\n)\n\n// NewSyslogOutput returns JSONOutputs in a LevelOutput with writers set to syslog\n// with the proper priority added to a LOG_USER facility.\n// If network and address are empty, Dial will connect to the local syslog server.\nfunc NewSyslogOutput(network, address, tag string) Output {\n\treturn NewSyslogOutputFacility(network, address, tag, syslog.LOG_USER)\n}\n\n// NewSyslogOutputFacility returns JSONOutputs in a LevelOutput with writers set to syslog\n// with the proper priority added to the passed facility.\n// If network and address are empty, Dial will connect to the local syslog server.\nfunc NewSyslogOutputFacility(network, address, tag string, facility syslog.Priority) Output {\n\to := LevelOutput{\n\t\tDebug: NewJSONOutput(NewSyslogWriter(network, address, facility|syslog.LOG_DEBUG, tag)),\n\t\tInfo:  NewJSONOutput(NewSyslogWriter(network, address, facility|syslog.LOG_INFO, tag)),\n\t\tWarn:  NewJSONOutput(NewSyslogWriter(network, address, facility|syslog.LOG_WARNING, tag)),\n\t\tError: NewJSONOutput(NewSyslogWriter(network, address, facility|syslog.LOG_ERR, tag)),\n\t}\n\treturn o\n}\n\n// NewSyslogWriter returns a writer ready to be used with output modules.\n// If network and address are empty, Dial will connect to the local syslog server.\nfunc NewSyslogWriter(network, address string, prio syslog.Priority, tag string) io.Writer {\n\ts, err := syslog.Dial(network, address, prio, tag)\n\tif err != nil {\n\t\tm := \"syslog dial error: \" + err.Error()\n\t\tcritialLogger.Print(m)\n\t\tpanic(m)\n\t}\n\treturn s\n}\n"
  },
  {
    "path": "output_test.go",
    "content": "package xlog\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype testOutput struct {\n\terr error\n\tw   chan map[string]interface{}\n}\n\nfunc newTestOutput() *testOutput {\n\treturn &testOutput{w: make(chan map[string]interface{}, 10)}\n}\n\nfunc newTestOutputErr(err error) *testOutput {\n\treturn &testOutput{w: make(chan map[string]interface{}, 10), err: err}\n}\n\nfunc (o *testOutput) Write(fields map[string]interface{}) (err error) {\n\to.w <- fields\n\treturn o.err\n}\n\nfunc (o *testOutput) reset() {\n\to.w = make(chan map[string]interface{}, 10)\n}\n\nfunc (o *testOutput) empty() bool {\n\tselect {\n\tcase <-o.w:\n\t\treturn false\n\tdefault:\n\t\treturn true\n\t}\n}\n\nfunc (o *testOutput) get() map[string]interface{} {\n\tselect {\n\tcase last := <-o.w:\n\t\treturn last\n\tcase <-time.After(2 * time.Second):\n\t\treturn nil\n\t}\n}\n\nfunc TestOutputChannel(t *testing.T) {\n\to := newTestOutput()\n\toc := NewOutputChannel(o)\n\tdefer oc.Close()\n\toc.input <- F{\"foo\": \"bar\"}\n\tassert.Equal(t, F{\"foo\": \"bar\"}, F(o.get()))\n}\n\nfunc TestOutputChannelError(t *testing.T) {\n\t// Trigger error path\n\tr, w := io.Pipe()\n\tgo func() {\n\t\tcritialLoggerMux.Lock()\n\t\tdefer critialLoggerMux.Unlock()\n\t\toldCritialLogger := critialLogger\n\t\tcritialLogger = log.New(w, \"\", 0)\n\t\to := newTestOutputErr(errors.New(\"some error\"))\n\t\toc := NewOutputChannel(o)\n\t\toc.input <- F{\"foo\": \"bar\"}\n\t\to.get()\n\t\toc.Close()\n\t\tcritialLogger = oldCritialLogger\n\t\tw.Close()\n\t}()\n\tb, err := ioutil.ReadAll(r)\n\tassert.NoError(t, err)\n\tassert.Contains(t, string(b), \"cannot write log message: some error\")\n}\n\nfunc TestOutputChannelClose(t *testing.T) {\n\toc := NewOutputChannel(newTestOutput())\n\tdefer oc.Close()\n\tassert.NotNil(t, oc.stop)\n\toc.Close()\n\tassert.Nil(t, oc.stop)\n\toc.Close()\n}\n\nfunc TestDiscard(t *testing.T) {\n\tassert.NoError(t, Discard.Write(F{}))\n}\n\nfunc TestMultiOutput(t *testing.T) {\n\to1 := newTestOutput()\n\to2 := newTestOutput()\n\tmo := MultiOutput{o1, o2}\n\terr := mo.Write(F{\"foo\": \"bar\"})\n\tassert.NoError(t, err)\n\tassert.Equal(t, F{\"foo\": \"bar\"}, F(<-o1.w))\n\tassert.Equal(t, F{\"foo\": \"bar\"}, F(<-o2.w))\n}\n\nfunc TestMultiOutputWithError(t *testing.T) {\n\to1 := newTestOutputErr(errors.New(\"some error\"))\n\to2 := newTestOutput()\n\tmo := MultiOutput{o1, o2}\n\terr := mo.Write(F{\"foo\": \"bar\"})\n\tassert.EqualError(t, err, \"some error\")\n\t// Still send data to all outputs\n\tassert.Equal(t, F{\"foo\": \"bar\"}, F(<-o1.w))\n\tassert.Equal(t, F{\"foo\": \"bar\"}, F(<-o2.w))\n}\n\nfunc TestFilterOutput(t *testing.T) {\n\to := newTestOutput()\n\tf := FilterOutput{\n\t\tCond: func(fields map[string]interface{}) bool {\n\t\t\treturn fields[\"foo\"] == \"bar\"\n\t\t},\n\t\tOutput: o,\n\t}\n\terr := f.Write(F{\"foo\": \"bar\"})\n\tassert.NoError(t, err)\n\tassert.Equal(t, F{\"foo\": \"bar\"}, F(o.get()))\n\n\to.reset()\n\terr = f.Write(F{\"foo\": \"baz\"})\n\tassert.NoError(t, err)\n\tassert.True(t, o.empty())\n\n\tf.Output = nil\n\terr = f.Write(F{\"foo\": \"baz\"})\n\tassert.NoError(t, err)\n}\n\nfunc TestLevelOutput(t *testing.T) {\n\toInfo := newTestOutput()\n\toError := newTestOutput()\n\toFatal := newTestOutput()\n\toWarn := &testOutput{err: errors.New(\"some error\")}\n\treset := func() {\n\t\toInfo.reset()\n\t\toError.reset()\n\t\toFatal.reset()\n\t\toWarn.reset()\n\t}\n\tl := LevelOutput{\n\t\tInfo:  oInfo,\n\t\tError: oError,\n\t\tFatal: oFatal,\n\t\tWarn:  oWarn,\n\t}\n\n\terr := l.Write(F{\"level\": \"fatal\", \"foo\": \"bar\"})\n\tassert.NoError(t, err)\n\tassert.True(t, oInfo.empty())\n\tassert.True(t, oError.empty())\n\tassert.Equal(t, F{\"level\": \"fatal\", \"foo\": \"bar\"}, F(<-oFatal.w))\n\tassert.True(t, oWarn.empty())\n\n\treset()\n\terr = l.Write(F{\"level\": \"error\", \"foo\": \"bar\"})\n\tassert.NoError(t, err)\n\tassert.True(t, oInfo.empty())\n\tassert.Equal(t, F{\"level\": \"error\", \"foo\": \"bar\"}, F(<-oError.w))\n\tassert.True(t, oFatal.empty())\n\tassert.True(t, oWarn.empty())\n\n\treset()\n\terr = l.Write(F{\"level\": \"info\", \"foo\": \"bar\"})\n\tassert.NoError(t, err)\n\tassert.Equal(t, F{\"level\": \"info\", \"foo\": \"bar\"}, F(<-oInfo.w))\n\tassert.True(t, oFatal.empty())\n\tassert.True(t, oError.empty())\n\tassert.True(t, oWarn.empty())\n\n\treset()\n\terr = l.Write(F{\"level\": \"warn\", \"foo\": \"bar\"})\n\tassert.EqualError(t, err, \"some error\")\n\tassert.True(t, oInfo.empty())\n\tassert.True(t, oError.empty())\n\tassert.True(t, oFatal.empty())\n\tassert.Equal(t, F{\"level\": \"warn\", \"foo\": \"bar\"}, F(<-oWarn.w))\n\n\treset()\n\terr = l.Write(F{\"level\": \"debug\", \"foo\": \"bar\"})\n\tassert.NoError(t, err)\n\tassert.True(t, oInfo.empty())\n\tassert.True(t, oError.empty())\n\tassert.True(t, oFatal.empty())\n\tassert.True(t, oWarn.empty())\n\n\treset()\n\terr = l.Write(F{\"foo\": \"bar\"})\n\tassert.NoError(t, err)\n\tassert.True(t, oInfo.empty())\n\tassert.True(t, oError.empty())\n\tassert.True(t, oFatal.empty())\n\tassert.True(t, oWarn.empty())\n}\n\nfunc TestSyslogOutput(t *testing.T) {\n\tbuf := bytes.NewBuffer(nil)\n\tcritialLoggerMux.Lock()\n\toldCritialLogger := critialLogger\n\tcritialLogger = log.New(buf, \"\", 0)\n\tdefer func() {\n\t\tcritialLogger = oldCritialLogger\n\t\tcritialLoggerMux.Unlock()\n\t}()\n\tm := NewSyslogOutput(\"udp\", \"127.0.0.1:1234\", \"mytag\")\n\tassert.IsType(t, LevelOutput{}, m)\n\tassert.Panics(t, func() {\n\t\tNewSyslogOutput(\"tcp\", \"an invalid host name\", \"mytag\")\n\t})\n\tassert.Regexp(t, \"syslog dial error: dial tcp:.*missing port in address.*\", buf.String())\n}\n\nfunc TestRecorderOutput(t *testing.T) {\n\to := RecorderOutput{}\n\to.Write(F{\"foo\": \"bar\"})\n\to.Write(F{\"bar\": \"baz\"})\n\tassert.Equal(t, []F{{\"foo\": \"bar\"}, {\"bar\": \"baz\"}}, o.Messages)\n\to.Reset()\n\tassert.Equal(t, []F{}, o.Messages)\n}\n\nfunc TestNewConsoleOutput(t *testing.T) {\n\told := isTerminal\n\tdefer func() { isTerminal = old }()\n\tisTerminal = func(w io.Writer) bool { return true }\n\tc := NewConsoleOutput()\n\tif assert.IsType(t, consoleOutput{}, c) {\n\t\tassert.Equal(t, os.Stderr, c.(consoleOutput).w)\n\t}\n\tisTerminal = func(w io.Writer) bool { return false }\n\tc = NewConsoleOutput()\n\tif assert.IsType(t, logfmtOutput{}, c) {\n\t\tassert.Equal(t, os.Stderr, c.(logfmtOutput).w)\n\t}\n}\n\nfunc TestNewConsoleOutputW(t *testing.T) {\n\tb := bytes.NewBuffer([]byte{})\n\tc := NewConsoleOutputW(b, NewLogfmtOutput(b))\n\tassert.IsType(t, logfmtOutput{}, c)\n\told := isTerminal\n\tdefer func() { isTerminal = old }()\n\tisTerminal = func(w io.Writer) bool { return true }\n\tc = NewConsoleOutputW(b, NewLogfmtOutput(b))\n\tif assert.IsType(t, consoleOutput{}, c) {\n\t\tassert.Equal(t, b, c.(consoleOutput).w)\n\t}\n}\n\nfunc TestConsoleOutput(t *testing.T) {\n\tbuf := &bytes.Buffer{}\n\tc := consoleOutput{w: buf}\n\terr := c.Write(F{\"message\": \"some message\", \"level\": \"info\", \"time\": time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC), \"foo\": \"bar\"})\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"2000/01/02 03:04:05 \\x1b[34mINFO\\x1b[0m some message \\x1b[32mfoo\\x1b[0m=bar\\n\", buf.String())\n\tbuf.Reset()\n\terr = c.Write(F{\"message\": \"some debug\", \"level\": \"debug\"})\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"\\x1b[37mDEBU\\x1b[0m some debug\\n\", buf.String())\n\tbuf.Reset()\n\terr = c.Write(F{\"message\": \"some warning\", \"level\": \"warn\"})\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"\\x1b[33mWARN\\x1b[0m some warning\\n\", buf.String())\n\tbuf.Reset()\n\terr = c.Write(F{\"message\": \"some error\", \"level\": \"error\"})\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"\\x1b[31mERRO\\x1b[0m some error\\n\", buf.String())\n}\n\nfunc TestLogfmtOutput(t *testing.T) {\n\tbuf := &bytes.Buffer{}\n\tc := NewLogfmtOutput(buf)\n\terr := c.Write(F{\n\t\t\"time\":    time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC),\n\t\t\"message\": \"some message\",\n\t\t\"level\":   \"info\",\n\t\t\"string\":  \"foo\",\n\t\t\"null\":    nil,\n\t\t\"quoted\":  \"needs \\\" quotes\",\n\t\t\"err\":     errors.New(\"error\"),\n\t\t\"errq\":    errors.New(\"error with \\\" quote\"),\n\t})\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"level=info message=\\\"some message\\\" time=\\\"2000-01-02 03:04:05 +0000 UTC\\\" err=error errq=\\\"error with \\\\\\\" quote\\\" null=null quoted=\\\"needs \\\\\\\" quotes\\\" string=foo\\n\", buf.String())\n}\n\nfunc TestJSONOutput(t *testing.T) {\n\tbuf := &bytes.Buffer{}\n\tj := NewJSONOutput(buf)\n\terr := j.Write(F{\"message\": \"some message\", \"level\": \"info\", \"foo\": \"bar\"})\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"{\\\"foo\\\":\\\"bar\\\",\\\"level\\\":\\\"info\\\",\\\"message\\\":\\\"some message\\\"}\\n\", buf.String())\n}\n\nfunc TestLogstashOutput(t *testing.T) {\n\tbuf := &bytes.Buffer{}\n\to := NewLogstashOutput(buf)\n\terr := o.Write(F{\n\t\t\"message\": \"some message\",\n\t\t\"level\":   \"info\",\n\t\t\"time\":    time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC),\n\t\t\"file\":    \"test.go:234\",\n\t\t\"foo\":     \"bar\",\n\t})\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"{\\\"@timestamp\\\":\\\"2000-01-02T03:04:05Z\\\",\\\"@version\\\":1,\\\"file\\\":\\\"test.go:234\\\",\\\"foo\\\":\\\"bar\\\",\\\"level\\\":\\\"INFO\\\",\\\"message\\\":\\\"some message\\\"}\", buf.String())\n}\n\nfunc TestUIDOutput(t *testing.T) {\n\to := newTestOutput()\n\ti := NewUIDOutput(\"id\", o)\n\terr := i.Write(F{\"message\": \"some message\", \"level\": \"info\", \"foo\": \"bar\"})\n\tlast := o.get()\n\tassert.NoError(t, err)\n\tassert.NotNil(t, last[\"id\"])\n\tassert.Len(t, last[\"id\"], 20)\n}\n\nfunc TestTrimOutput(t *testing.T) {\n\to := newTestOutput()\n\ti := NewTrimOutput(10, o)\n\terr := i.Write(F{\"short\": \"short\", \"long\": \"too long message\", \"number\": 20})\n\tlast := o.get()\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"short\", last[\"short\"])\n\tassert.Equal(t, \"too long m\", last[\"long\"])\n\tassert.Equal(t, 20, last[\"number\"])\n}\n\nfunc TestTrimFieldsOutput(t *testing.T) {\n\to := newTestOutput()\n\ti := NewTrimFieldsOutput([]string{\"short\", \"trim\", \"number\"}, 10, o)\n\terr := i.Write(F{\"short\": \"short\", \"long\": \"too long message\", \"trim\": \"too long message\", \"number\": 20})\n\tlast := o.get()\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"short\", last[\"short\"])\n\tassert.Equal(t, \"too long m\", last[\"trim\"])\n\tassert.Equal(t, \"too long message\", last[\"long\"])\n\tassert.Equal(t, 20, last[\"number\"])\n}\n"
  },
  {
    "path": "std.go",
    "content": "package xlog\n\nimport \"fmt\"\n\nvar std = New(Config{\n\tOutput: NewConsoleOutput(),\n})\n\n// SetLogger changes the global logger instance\nfunc SetLogger(logger Logger) {\n\tstd = logger\n}\n\n// Debug calls the Debug() method on the default logger\nfunc Debug(v ...interface{}) {\n\tf := extractFields(&v)\n\tstd.OutputF(LevelDebug, 2, fmt.Sprint(v...), f)\n}\n\n// Debugf calls the Debugf() method on the default logger\nfunc Debugf(format string, v ...interface{}) {\n\tf := extractFields(&v)\n\tstd.OutputF(LevelDebug, 2, fmt.Sprintf(format, v...), f)\n}\n\n// Info calls the Info() method on the default logger\nfunc Info(v ...interface{}) {\n\tf := extractFields(&v)\n\tstd.OutputF(LevelInfo, 2, fmt.Sprint(v...), f)\n}\n\n// Infof calls the Infof() method on the default logger\nfunc Infof(format string, v ...interface{}) {\n\tf := extractFields(&v)\n\tstd.OutputF(LevelInfo, 2, fmt.Sprintf(format, v...), f)\n}\n\n// Warn calls the Warn() method on the default logger\nfunc Warn(v ...interface{}) {\n\tf := extractFields(&v)\n\tstd.OutputF(LevelWarn, 2, fmt.Sprint(v...), f)\n}\n\n// Warnf calls the Warnf() method on the default logger\nfunc Warnf(format string, v ...interface{}) {\n\tf := extractFields(&v)\n\tstd.OutputF(LevelWarn, 2, fmt.Sprintf(format, v...), f)\n}\n\n// Error calls the Error() method on the default logger\nfunc Error(v ...interface{}) {\n\tf := extractFields(&v)\n\tstd.OutputF(LevelError, 2, fmt.Sprint(v...), f)\n}\n\n// Errorf calls the Errorf() method on the default logger\n//\n// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last\n// argument to workaround go vet false alarm.\nfunc Errorf(format string, v ...interface{}) {\n\tf := extractFields(&v)\n\tif f != nil {\n\t\t// Let user add a %v at the end of the message when fields are passed to satisfy go vet\n\t\tl := len(format)\n\t\tif l > 2 && format[l-2] == '%' && format[l-1] == 'v' {\n\t\t\tformat = format[0 : l-2]\n\t\t}\n\t}\n\tstd.OutputF(LevelError, 2, fmt.Sprintf(format, v...), f)\n}\n\n// Fatal calls the Fatal() method on the default logger\nfunc Fatal(v ...interface{}) {\n\tf := extractFields(&v)\n\tstd.OutputF(LevelFatal, 2, fmt.Sprint(v...), f)\n\tif l, ok := std.(*logger); ok {\n\t\tif o, ok := l.output.(*OutputChannel); ok {\n\t\t\to.Close()\n\t\t}\n\t}\n\texit1()\n}\n\n// Fatalf calls the Fatalf() method on the default logger\n//\n// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last\n// argument to workaround go vet false alarm.\nfunc Fatalf(format string, v ...interface{}) {\n\tf := extractFields(&v)\n\tif f != nil {\n\t\t// Let user add a %v at the end of the message when fields are passed to satisfy go vet\n\t\tl := len(format)\n\t\tif l > 2 && format[l-2] == '%' && format[l-1] == 'v' {\n\t\t\tformat = format[0 : l-2]\n\t\t}\n\t}\n\tstd.OutputF(LevelFatal, 2, fmt.Sprintf(format, v...), f)\n\tif l, ok := std.(*logger); ok {\n\t\tif o, ok := l.output.(*OutputChannel); ok {\n\t\t\to.Close()\n\t\t}\n\t}\n\texit1()\n}\n"
  },
  {
    "path": "std_example_test.go",
    "content": "package xlog_test\n\nimport \"github.com/rs/xlog\"\n\nfunc ExampleSetLogger() {\n\txlog.SetLogger(xlog.New(xlog.Config{\n\t\tLevel:  xlog.LevelInfo,\n\t\tOutput: xlog.NewConsoleOutput(),\n\t\tFields: xlog.F{\n\t\t\t\"role\": \"my-service\",\n\t\t},\n\t}))\n}\n"
  },
  {
    "path": "std_test.go",
    "content": "package xlog\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestGlobalLogger(t *testing.T) {\n\to := newTestOutput()\n\toldStd := std\n\tdefer func() { std = oldStd }()\n\tSetLogger(New(Config{Output: o}))\n\tDebug(\"test\")\n\tlast := o.get()\n\tassert.Equal(t, \"test\", last[\"message\"])\n\tassert.Equal(t, \"debug\", last[\"level\"])\n\to.reset()\n\tDebugf(\"test\")\n\tlast = o.get()\n\tassert.Equal(t, \"test\", last[\"message\"])\n\tassert.Equal(t, \"debug\", last[\"level\"])\n\to.reset()\n\tInfo(\"test\")\n\tlast = o.get()\n\tassert.Equal(t, \"test\", last[\"message\"])\n\tassert.Equal(t, \"info\", last[\"level\"])\n\to.reset()\n\tInfof(\"test\")\n\tlast = o.get()\n\tassert.Equal(t, \"test\", last[\"message\"])\n\tassert.Equal(t, \"info\", last[\"level\"])\n\to.reset()\n\tWarn(\"test\")\n\tlast = o.get()\n\tassert.Equal(t, \"test\", last[\"message\"])\n\tassert.Equal(t, \"warn\", last[\"level\"])\n\to.reset()\n\tWarnf(\"test\")\n\tlast = o.get()\n\tassert.Equal(t, \"test\", last[\"message\"])\n\tassert.Equal(t, \"warn\", last[\"level\"])\n\to.reset()\n\tError(\"test\")\n\tlast = o.get()\n\tassert.Equal(t, \"test\", last[\"message\"])\n\tassert.Equal(t, \"error\", last[\"level\"])\n\to.reset()\n\tErrorf(\"test\")\n\tlast = o.get()\n\tassert.Equal(t, \"test\", last[\"message\"])\n\tassert.Equal(t, \"error\", last[\"level\"])\n\to.reset()\n\toldExit := exit1\n\texit1 = func() {}\n\tdefer func() { exit1 = oldExit }()\n\tFatal(\"test\")\n\tlast = o.get()\n\tassert.Equal(t, \"test\", last[\"message\"])\n\tassert.Equal(t, \"fatal\", last[\"level\"])\n\to.reset()\n\tFatalf(\"test\")\n\tlast = o.get()\n\tassert.Equal(t, \"test\", last[\"message\"])\n\tassert.Equal(t, \"fatal\", last[\"level\"])\n\to.reset()\n}\n\nfunc TestStdError(t *testing.T) {\n\to := newTestOutput()\n\toldStd := std\n\tdefer func() { std = oldStd }()\n\tSetLogger(New(Config{Output: o}))\n\tError(\"test\", F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"std_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"error\", \"message\": \"test\", \"foo\": \"bar\"}, last)\n}\n\nfunc TestStdErrorf(t *testing.T) {\n\to := newTestOutput()\n\toldStd := std\n\tdefer func() { std = oldStd }()\n\tSetLogger(New(Config{Output: o}))\n\tErrorf(\"test %d%v\", 1, F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"std_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"error\", \"message\": \"test 1\", \"foo\": \"bar\"}, last)\n}\n\nfunc TestStdFatal(t *testing.T) {\n\te := exit1\n\texited := 0\n\texit1 = func() { exited++ }\n\tdefer func() { exit1 = e }()\n\to := newTestOutput()\n\toldStd := std\n\tdefer func() { std = oldStd }()\n\tSetLogger(New(Config{Output: o}))\n\tFatal(\"test\", F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"std_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"fatal\", \"message\": \"test\", \"foo\": \"bar\"}, last)\n\tassert.Equal(t, 1, exited)\n}\n\nfunc TestStdFatalf(t *testing.T) {\n\te := exit1\n\texited := 0\n\texit1 = func() { exited++ }\n\tdefer func() { exit1 = e }()\n\to := newTestOutput()\n\toldStd := std\n\tdefer func() { std = oldStd }()\n\tSetLogger(New(Config{Output: o}))\n\tFatalf(\"test %d%v\", 1, F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"std_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"fatal\", \"message\": \"test 1\", \"foo\": \"bar\"}, last)\n\tassert.Equal(t, 1, exited)\n}\n"
  },
  {
    "path": "util.go",
    "content": "package xlog\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n)\n\ntype color int\n\nconst (\n\tred    color = 31\n\tgreen  color = 32\n\tyellow color = 33\n\tblue   color = 34\n\tgray   color = 37\n)\n\nfunc colorPrint(w io.Writer, s string, c color) {\n\tw.Write([]byte{0x1b, '[', byte('0' + c/10), byte('0' + c%10), 'm'})\n\tw.Write([]byte(s))\n\tw.Write([]byte(\"\\x1b[0m\"))\n}\n\nfunc needsQuotedValueRune(r rune) bool {\n\treturn r <= ' ' || r == '=' || r == '\"'\n}\n\n// writeValue writes a value on the writer in a logfmt compatible way\nfunc writeValue(w io.Writer, v interface{}) (err error) {\n\tswitch v := v.(type) {\n\tcase nil:\n\t\t_, err = w.Write([]byte(\"null\"))\n\tcase string:\n\t\tif strings.IndexFunc(v, needsQuotedValueRune) != -1 {\n\t\t\tvar b []byte\n\t\t\tb, err = json.Marshal(v)\n\t\t\tif err == nil {\n\t\t\t\tw.Write(b)\n\t\t\t}\n\t\t} else {\n\t\t\t_, err = w.Write([]byte(v))\n\t\t}\n\tcase error:\n\t\ts := v.Error()\n\t\terr = writeValue(w, s)\n\tdefault:\n\t\ts := fmt.Sprint(v)\n\t\terr = writeValue(w, s)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "util_test.go",
    "content": "package xlog\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestColorPrint(t *testing.T) {\n\tbuf := &bytes.Buffer{}\n\tcolorPrint(buf, \"test\", red)\n\tassert.Equal(t, \"\\x1b[31mtest\\x1b[0m\", buf.String())\n\tbuf.Reset()\n\tcolorPrint(buf, \"test\", green)\n\tassert.Equal(t, \"\\x1b[32mtest\\x1b[0m\", buf.String())\n\tbuf.Reset()\n\tcolorPrint(buf, \"test\", yellow)\n\tassert.Equal(t, \"\\x1b[33mtest\\x1b[0m\", buf.String())\n\tbuf.Reset()\n\tcolorPrint(buf, \"test\", blue)\n\tassert.Equal(t, \"\\x1b[34mtest\\x1b[0m\", buf.String())\n\tbuf.Reset()\n\tcolorPrint(buf, \"test\", gray)\n\tassert.Equal(t, \"\\x1b[37mtest\\x1b[0m\", buf.String())\n}\n\nfunc TestNeedsQuotedValueRune(t *testing.T) {\n\tassert.True(t, needsQuotedValueRune('='))\n\tassert.True(t, needsQuotedValueRune('\"'))\n\tassert.True(t, needsQuotedValueRune(' '))\n\tassert.False(t, needsQuotedValueRune('a'))\n\tassert.False(t, needsQuotedValueRune('\\''))\n}\n\nfunc TestWriteValue(t *testing.T) {\n\tbuf := &bytes.Buffer{}\n\twrite := func(v interface{}) string {\n\t\tbuf.Reset()\n\t\terr := writeValue(buf, v)\n\t\tif err == nil {\n\t\t\treturn buf.String()\n\t\t}\n\t\treturn \"\"\n\t}\n\tassert.Equal(t, `foobar`, write(`foobar`))\n\tassert.Equal(t, `\"foo=bar\"`, write(`foo=bar`))\n\tassert.Equal(t, `\"foo bar\"`, write(`foo bar`))\n\tassert.Equal(t, `\"foo\\\"bar\"`, write(`foo\"bar`))\n\tassert.Equal(t, `\"foo\\nbar\"`, write(\"foo\\nbar\"))\n\tassert.Equal(t, `null`, write(nil))\n\tassert.Equal(t, `\"2000-01-02 03:04:05 +0000 UTC\"`, write(time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC)))\n\tassert.Equal(t, `\"error \\\"with quote\\\"\"`, write(errors.New(`error \"with quote\"`)))\n}\n"
  },
  {
    "path": "xlog.go",
    "content": "// Package xlog is a logger coupled with HTTP net/context aware middleware.\n//\n// Unlike most loggers, xlog will never block your application because one its\n// outputs is lagging. The log commands are connected to their outputs through\n// a buffered channel and will prefer to discard messages if the buffer get full.\n// All message formatting, serialization and transport happen in a dedicated go\n// routine.\n//\n// Features:\n//\n//     - Per request log context\n//     - Per request and/or per message key/value fields\n//     - Log levels (Debug, Info, Warn, Error)\n//     - Color output when terminal is detected\n//     - Custom output (JSON, logfmt, …)\n//     - Automatic gathering of request context like User-Agent, IP etc.\n//     - Drops message rather than blocking execution\n//     - Easy access logging thru github.com/rs/xaccess\n//\n// It works best in combination with github.com/rs/xhandler.\npackage xlog // import \"github.com/rs/xlog\"\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Logger defines the interface for a xlog compatible logger\ntype Logger interface {\n\t// Implements io.Writer so it can be set a output of log.Logger\n\tio.Writer\n\n\t// SetField sets a field on the logger's context. All future messages on this logger\n\t// will have this field set.\n\tSetField(name string, value interface{})\n\t// GetFields returns all the fields set on the logger\n\tGetFields() F\n\t// Debug logs a debug message. If last parameter is a map[string]string, it's content\n\t// is added as fields to the message.\n\tDebug(v ...interface{})\n\t// Debug logs a debug message with format. If last parameter is a map[string]string,\n\t// it's content is added as fields to the message.\n\tDebugf(format string, v ...interface{})\n\t// Info logs a info message. If last parameter is a map[string]string, it's content\n\t// is added as fields to the message.\n\tInfo(v ...interface{})\n\t// Info logs a info message with format. If last parameter is a map[string]string,\n\t// it's content is added as fields to the message.\n\tInfof(format string, v ...interface{})\n\t// Warn logs a warning message. If last parameter is a map[string]string, it's content\n\t// is added as fields to the message.\n\tWarn(v ...interface{})\n\t// Warn logs a warning message with format. If last parameter is a map[string]string,\n\t// it's content is added as fields to the message.\n\tWarnf(format string, v ...interface{})\n\t// Error logs an error message. If last parameter is a map[string]string, it's content\n\t// is added as fields to the message.\n\tError(v ...interface{})\n\t// Error logs an error message with format. If last parameter is a map[string]string,\n\t// it's content is added as fields to the message.\n\tErrorf(format string, v ...interface{})\n\t// Fatal logs an error message followed by a call to os.Exit(1). If last parameter is a\n\t// map[string]string, it's content is added as fields to the message.\n\tFatal(v ...interface{})\n\t// Fatalf logs an error message with format followed by a call to ox.Exit(1). If last\n\t// parameter is a map[string]string, it's content is added as fields to the message.\n\tFatalf(format string, v ...interface{})\n\t// Output mimics std logger interface\n\tOutput(calldepth int, s string) error\n\t// OutputF outputs message with fields.\n\tOutputF(level Level, calldepth int, msg string, fields map[string]interface{})\n}\n\n// LoggerCopier defines a logger with copy support\ntype LoggerCopier interface {\n\t// Copy returns a copy of the logger\n\tCopy() Logger\n}\n\n// Config defines logger's configuration\ntype Config struct {\n\t// Level is the maximum level to output, logs with lower level are discarded.\n\tLevel Level\n\t// Fields defines default fields to use with all messages.\n\tFields map[string]interface{}\n\t// Output to use to write log messages to.\n\t//\n\t// You should always wrap your output with an OutputChannel otherwise your\n\t// logger will be connected to its output synchronously.\n\tOutput Output\n\t// DisablePooling removes the use of a sync.Pool for cases where logger\n\t// instances are needed beyond the scope of a request handler. This option\n\t// puts a greater pressure on GC and increases the amount of memory allocated\n\t// and freed. Use only if persistent loggers are a requirement.\n\tDisablePooling bool\n}\n\n// F represents a set of log message fields\ntype F map[string]interface{}\n\ntype logger struct {\n\tlevel          Level\n\toutput         Output\n\tfields         F\n\tdisablePooling bool\n}\n\n// Common field names for log messages.\nvar (\n\tKeyTime    = \"time\"\n\tKeyMessage = \"message\"\n\tKeyLevel   = \"level\"\n\tKeyFile    = \"file\"\n)\n\nvar now = time.Now\nvar exit1 = func() { os.Exit(1) }\n\n// critialLogger is a logger to use when xlog is not able to deliver a message\nvar critialLogger = log.New(os.Stderr, \"xlog: \", log.Ldate|log.Ltime|log.LUTC|log.Lshortfile)\n\nvar loggerPool = &sync.Pool{\n\tNew: func() interface{} {\n\t\treturn &logger{}\n\t},\n}\n\n// New manually creates a logger.\n//\n// This function should only be used out of a request. Use FromContext in request.\nfunc New(c Config) Logger {\n\tvar l *logger\n\tif c.DisablePooling {\n\t\tl = &logger{}\n\t} else {\n\t\tl = loggerPool.Get().(*logger)\n\t}\n\tl.level = c.Level\n\tl.output = c.Output\n\tif l.output == nil {\n\t\tl.output = NewOutputChannel(NewConsoleOutput())\n\t}\n\tfor k, v := range c.Fields {\n\t\tl.SetField(k, v)\n\t}\n\tl.disablePooling = c.DisablePooling\n\treturn l\n}\n\n// Copy returns a copy of the passed logger if the logger implements\n// LoggerCopier or the NopLogger otherwise.\nfunc Copy(l Logger) Logger {\n\tif l, ok := l.(LoggerCopier); ok {\n\t\treturn l.Copy()\n\t}\n\treturn NopLogger\n}\n\n// Copy returns a copy of the logger\nfunc (l *logger) Copy() Logger {\n\tl2 := &logger{\n\t\tlevel:          l.level,\n\t\toutput:         l.output,\n\t\tfields:         map[string]interface{}{},\n\t\tdisablePooling: l.disablePooling,\n\t}\n\tfor k, v := range l.fields {\n\t\tl2.fields[k] = v\n\t}\n\treturn l2\n}\n\n// close returns the logger to the pool for reuse\nfunc (l *logger) close() {\n\tif !l.disablePooling {\n\t\tl.level = 0\n\t\tl.output = nil\n\t\tl.fields = nil\n\t\tloggerPool.Put(l)\n\t}\n}\n\nfunc (l *logger) send(level Level, calldepth int, msg string, fields map[string]interface{}) {\n\tif level < l.level || l.output == nil {\n\t\treturn\n\t}\n\tdata := make(map[string]interface{}, 4+len(fields)+len(l.fields))\n\tdata[KeyTime] = now()\n\tdata[KeyLevel] = level.String()\n\tdata[KeyMessage] = msg\n\tif _, file, line, ok := runtime.Caller(calldepth); ok {\n\t\tdata[KeyFile] = path.Base(file) + \":\" + strconv.FormatInt(int64(line), 10)\n\t}\n\tfor k, v := range fields {\n\t\tdata[k] = v\n\t}\n\tif l.fields != nil {\n\t\tfor k, v := range l.fields {\n\t\t\tdata[k] = v\n\t\t}\n\t}\n\tif err := l.output.Write(data); err != nil {\n\t\tcritialLogger.Print(\"send error: \", err.Error())\n\t}\n}\n\nfunc extractFields(v *[]interface{}) map[string]interface{} {\n\tif l := len(*v); l > 0 {\n\t\tif f, ok := (*v)[l-1].(map[string]interface{}); ok {\n\t\t\t*v = (*v)[:l-1]\n\t\t\treturn f\n\t\t}\n\t\tif f, ok := (*v)[l-1].(F); ok {\n\t\t\t*v = (*v)[:l-1]\n\t\t\treturn f\n\t\t}\n\t}\n\treturn nil\n}\n\n// SetField implements Logger interface\nfunc (l *logger) SetField(name string, value interface{}) {\n\tif l.fields == nil {\n\t\tl.fields = map[string]interface{}{}\n\t}\n\tl.fields[name] = value\n}\n\n// GetFields implements Logger interface\nfunc (l *logger) GetFields() F {\n\treturn l.fields\n}\n\n// Output implements Logger interface\nfunc (l *logger) OutputF(level Level, calldepth int, msg string, fields map[string]interface{}) {\n\tl.send(level, calldepth+1, msg, fields)\n}\n\n// Debug implements Logger interface\nfunc (l *logger) Debug(v ...interface{}) {\n\tf := extractFields(&v)\n\tl.send(LevelDebug, 2, fmt.Sprint(v...), f)\n}\n\n// Debugf implements Logger interface\nfunc (l *logger) Debugf(format string, v ...interface{}) {\n\tf := extractFields(&v)\n\tl.send(LevelDebug, 2, fmt.Sprintf(format, v...), f)\n}\n\n// Info implements Logger interface\nfunc (l *logger) Info(v ...interface{}) {\n\tf := extractFields(&v)\n\tl.send(LevelInfo, 2, fmt.Sprint(v...), f)\n}\n\n// Infof implements Logger interface\nfunc (l *logger) Infof(format string, v ...interface{}) {\n\tf := extractFields(&v)\n\tl.send(LevelInfo, 2, fmt.Sprintf(format, v...), f)\n}\n\n// Warn implements Logger interface\nfunc (l *logger) Warn(v ...interface{}) {\n\tf := extractFields(&v)\n\tl.send(LevelWarn, 2, fmt.Sprint(v...), f)\n}\n\n// Warnf implements Logger interface\nfunc (l *logger) Warnf(format string, v ...interface{}) {\n\tf := extractFields(&v)\n\tl.send(LevelWarn, 2, fmt.Sprintf(format, v...), f)\n}\n\n// Error implements Logger interface\nfunc (l *logger) Error(v ...interface{}) {\n\tf := extractFields(&v)\n\tl.send(LevelError, 2, fmt.Sprint(v...), f)\n}\n\n// Errorf implements Logger interface\n//\n// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last\n// argument to workaround go vet false alarm.\nfunc (l *logger) Errorf(format string, v ...interface{}) {\n\tf := extractFields(&v)\n\tif f != nil {\n\t\t// Let user add a %v at the end of the message when fields are passed to satisfy go vet\n\t\tl := len(format)\n\t\tif l > 2 && format[l-2] == '%' && format[l-1] == 'v' {\n\t\t\tformat = format[0 : l-2]\n\t\t}\n\t}\n\tl.send(LevelError, 2, fmt.Sprintf(format, v...), f)\n}\n\n// Fatal implements Logger interface\nfunc (l *logger) Fatal(v ...interface{}) {\n\tf := extractFields(&v)\n\tl.send(LevelFatal, 2, fmt.Sprint(v...), f)\n\tif o, ok := l.output.(*OutputChannel); ok {\n\t\to.Close()\n\t}\n\texit1()\n}\n\n// Fatalf implements Logger interface\n//\n// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last\n// argument to workaround go vet false alarm.\nfunc (l *logger) Fatalf(format string, v ...interface{}) {\n\tf := extractFields(&v)\n\tif f != nil {\n\t\t// Let user add a %v at the end of the message when fields are passed to satisfy go vet\n\t\tl := len(format)\n\t\tif l > 2 && format[l-2] == '%' && format[l-1] == 'v' {\n\t\t\tformat = format[0 : l-2]\n\t\t}\n\t}\n\tl.send(LevelFatal, 2, fmt.Sprintf(format, v...), f)\n\tif o, ok := l.output.(*OutputChannel); ok {\n\t\to.Close()\n\t}\n\texit1()\n}\n\n// Write implements io.Writer interface\nfunc (l *logger) Write(p []byte) (int, error) {\n\tmsg := strings.TrimRight(string(p), \"\\n\")\n\tl.send(LevelInfo, 4, msg, nil)\n\tif o, ok := l.output.(*OutputChannel); ok {\n\t\to.Flush()\n\t}\n\treturn len(p), nil\n}\n\n// Output implements common logger interface\nfunc (l *logger) Output(calldepth int, s string) error {\n\tl.send(LevelInfo, 2, s, nil)\n\treturn nil\n}\n"
  },
  {
    "path": "xlog_bench_test.go",
    "content": "package xlog\n\nimport \"testing\"\n\nfunc BenchmarkSend(b *testing.B) {\n\tl := New(Config{Output: Discard, Fields: F{\"a\": \"b\"}}).(*logger)\n\tb.ResetTimer()\n\tb.ReportAllocs()\n\tfor i := 0; i < b.N; i++ {\n\t\tl.send(0, 0, \"test\", F{\"foo\": \"bar\", \"bar\": \"baz\"})\n\t}\n}\n"
  },
  {
    "path": "xlog_examples_test.go",
    "content": "// +build go1.7\n\npackage xlog_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"log\"\n\n\t\"github.com/rs/xlog\"\n)\n\nfunc Example_log() {\n\tctx := context.TODO()\n\tl := xlog.FromContext(ctx)\n\n\t// Log a simple message\n\tl.Debug(\"message\")\n\n\tif err := errors.New(\"some error\"); err != nil {\n\t\tl.Errorf(\"Some error happened: %v\", err)\n\t}\n\n\t// With optional fields\n\tl.Debugf(\"foo %s\", \"bar\", xlog.F{\n\t\t\"field\": \"value\",\n\t})\n}\n\nfunc Example_stdlog() {\n\t// Define logger conf\n\tconf := xlog.Config{\n\t\tOutput: xlog.NewConsoleOutput(),\n\t}\n\n\t// Remove timestamp and other decorations of the std logger\n\tlog.SetFlags(0)\n\n\t// Plug a xlog instance to Go's std logger\n\tlog.SetOutput(xlog.New(conf))\n}\n"
  },
  {
    "path": "xlog_test.go",
    "content": "package xlog\n\nimport (\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar fakeNow = time.Date(0, 0, 0, 0, 0, 0, 0, time.Local)\nvar critialLoggerMux = sync.Mutex{}\n\nfunc init() {\n\tnow = func() time.Time {\n\t\treturn fakeNow\n\t}\n}\n\nfunc TestNew(t *testing.T) {\n\toc := NewOutputChannel(newTestOutput())\n\tdefer oc.Close()\n\tc := Config{\n\t\tLevel:  LevelError,\n\t\tOutput: oc,\n\t\tFields: F{\"foo\": \"bar\"},\n\t}\n\tL := New(c)\n\tl, ok := L.(*logger)\n\tif assert.True(t, ok) {\n\t\tassert.Equal(t, LevelError, l.level)\n\t\tassert.Equal(t, c.Output, l.output)\n\t\tassert.Equal(t, F{\"foo\": \"bar\"}, F(l.fields))\n\t\t// Ensure l.fields is a clone\n\t\tc.Fields[\"bar\"] = \"baz\"\n\t\tassert.Equal(t, F{\"foo\": \"bar\"}, F(l.fields))\n\t\tassert.Equal(t, false, l.disablePooling)\n\t\tl.close()\n\t}\n}\n\nfunc TestNewPoolDisabled(t *testing.T) {\n\toc := NewOutputChannel(newTestOutput())\n\tdefer oc.Close()\n\toriginalPool := loggerPool\n\tdefer func(p *sync.Pool) {\n\t\tloggerPool = originalPool\n\t}(originalPool)\n\tloggerPool = &sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\tassert.Fail(t, \"pool used when disabled\")\n\t\t\treturn nil\n\t\t},\n\t}\n\tc := Config{\n\t\tLevel:          LevelError,\n\t\tOutput:         oc,\n\t\tFields:         F{\"foo\": \"bar\"},\n\t\tDisablePooling: true,\n\t}\n\tL := New(c)\n\tl, ok := L.(*logger)\n\tif assert.True(t, ok) {\n\t\tassert.Equal(t, LevelError, l.level)\n\t\tassert.Equal(t, c.Output, l.output)\n\t\tassert.Equal(t, F{\"foo\": \"bar\"}, F(l.fields))\n\t\t// Ensure l.fields is a clone\n\t\tc.Fields[\"bar\"] = \"baz\"\n\t\tassert.Equal(t, F{\"foo\": \"bar\"}, F(l.fields))\n\t\tassert.Equal(t, true, l.disablePooling)\n\t\tl.close()\n\t\t// Assert again to ensure close does not remove internal state\n\t\tassert.Equal(t, LevelError, l.level)\n\t\tassert.Equal(t, c.Output, l.output)\n\t\tassert.Equal(t, F{\"foo\": \"bar\"}, F(l.fields))\n\t\t// Ensure l.fields is a clone\n\t\tc.Fields[\"bar\"] = \"baz\"\n\t\tassert.Equal(t, F{\"foo\": \"bar\"}, F(l.fields))\n\t\tassert.Equal(t, true, l.disablePooling)\n\t}\n}\n\nfunc TestCopy(t *testing.T) {\n\toc := NewOutputChannel(newTestOutput())\n\tdefer oc.Close()\n\tc := Config{\n\t\tLevel:  LevelError,\n\t\tOutput: oc,\n\t\tFields: F{\"foo\": \"bar\"},\n\t}\n\tl := New(c).(*logger)\n\tl2 := Copy(l).(*logger)\n\tassert.Equal(t, l.output, l2.output)\n\tassert.Equal(t, l.level, l2.level)\n\tassert.Equal(t, l.fields, l2.fields)\n\tl2.SetField(\"bar\", \"baz\")\n\tassert.Equal(t, F{\"foo\": \"bar\"}, l.fields)\n\tassert.Equal(t, F{\"foo\": \"bar\", \"bar\": \"baz\"}, l2.fields)\n\n\tassert.Equal(t, NopLogger, Copy(NopLogger))\n\tassert.Equal(t, NopLogger, Copy(nil))\n}\n\nfunc TestNewDefautOutput(t *testing.T) {\n\tL := New(Config{})\n\tl, ok := L.(*logger)\n\tif assert.True(t, ok) {\n\t\tassert.NotNil(t, l.output)\n\t\tl.close()\n\t}\n}\n\nfunc TestSend(t *testing.T) {\n\to := newTestOutput()\n\tl := New(Config{Output: o}).(*logger)\n\tl.send(LevelDebug, 1, \"test\", F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"debug\", \"message\": \"test\", \"foo\": \"bar\"}, last)\n\n\tl.SetField(\"bar\", \"baz\")\n\tl.send(LevelInfo, 1, \"test\", F{\"foo\": \"bar\"})\n\tlast = <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"info\", \"message\": \"test\", \"foo\": \"bar\", \"bar\": \"baz\"}, last)\n\n\tl = New(Config{Output: o, Level: 1}).(*logger)\n\to.reset()\n\tl.send(0, 2, \"test\", F{\"foo\": \"bar\"})\n\tassert.True(t, o.empty())\n}\n\nfunc TestSendDrop(t *testing.T) {\n\tt.Skip()\n\tr, w := io.Pipe()\n\tgo func() {\n\t\tcritialLoggerMux.Lock()\n\t\tdefer critialLoggerMux.Unlock()\n\t\toldCritialLogger := critialLogger\n\t\tcritialLogger = log.New(w, \"\", 0)\n\t\to := newTestOutput()\n\t\toc := NewOutputChannelBuffer(Discard, 1)\n\t\tl := New(Config{Output: oc}).(*logger)\n\t\tl.send(LevelDebug, 2, \"test\", F{\"foo\": \"bar\"})\n\t\tl.send(LevelDebug, 2, \"test\", F{\"foo\": \"bar\"})\n\t\tl.send(LevelDebug, 2, \"test\", F{\"foo\": \"bar\"})\n\t\to.get()\n\t\to.get()\n\t\to.get()\n\t\toc.Close()\n\t\tcritialLogger = oldCritialLogger\n\t\tw.Close()\n\t}()\n\tb, err := ioutil.ReadAll(r)\n\tassert.NoError(t, err)\n\tassert.Contains(t, string(b), \"send error: buffer full\")\n}\n\nfunc TestExtractFields(t *testing.T) {\n\tv := []interface{}{\"a\", 1, map[string]interface{}{\"foo\": \"bar\"}}\n\tf := extractFields(&v)\n\tassert.Equal(t, map[string]interface{}{\"foo\": \"bar\"}, f)\n\tassert.Equal(t, []interface{}{\"a\", 1}, v)\n\n\tv = []interface{}{map[string]interface{}{\"foo\": \"bar\"}, \"a\", 1}\n\tf = extractFields(&v)\n\tassert.Nil(t, f)\n\tassert.Equal(t, []interface{}{map[string]interface{}{\"foo\": \"bar\"}, \"a\", 1}, v)\n\n\tv = []interface{}{\"a\", 1, F{\"foo\": \"bar\"}}\n\tf = extractFields(&v)\n\tassert.Equal(t, map[string]interface{}{\"foo\": \"bar\"}, f)\n\tassert.Equal(t, []interface{}{\"a\", 1}, v)\n\n\tv = []interface{}{}\n\tf = extractFields(&v)\n\tassert.Nil(t, f)\n\tassert.Equal(t, []interface{}{}, v)\n}\n\nfunc TestGetFields(t *testing.T) {\n\toc := NewOutputChannelBuffer(Discard, 1)\n\tl := New(Config{Output: oc}).(*logger)\n\tl.SetField(\"k\", \"v\")\n\tassert.Equal(t, F{\"k\": \"v\"}, l.GetFields())\n}\n\nfunc TestDebug(t *testing.T) {\n\to := newTestOutput()\n\tl := New(Config{Output: o}).(*logger)\n\tl.Debug(\"test\", F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"debug\", \"message\": \"test\", \"foo\": \"bar\"}, last)\n}\n\nfunc TestDebugf(t *testing.T) {\n\to := newTestOutput()\n\tl := New(Config{Output: o}).(*logger)\n\tl.Debugf(\"test %d\", 1, F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"debug\", \"message\": \"test 1\", \"foo\": \"bar\"}, last)\n}\n\nfunc TestInfo(t *testing.T) {\n\to := newTestOutput()\n\tl := New(Config{Output: o}).(*logger)\n\tl.Info(\"test\", F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"info\", \"message\": \"test\", \"foo\": \"bar\"}, last)\n}\n\nfunc TestInfof(t *testing.T) {\n\to := newTestOutput()\n\tl := New(Config{Output: o}).(*logger)\n\tl.Infof(\"test %d\", 1, F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"info\", \"message\": \"test 1\", \"foo\": \"bar\"}, last)\n}\n\nfunc TestWarn(t *testing.T) {\n\to := newTestOutput()\n\tl := New(Config{Output: o}).(*logger)\n\tl.Warn(\"test\", F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"warn\", \"message\": \"test\", \"foo\": \"bar\"}, last)\n}\n\nfunc TestWarnf(t *testing.T) {\n\to := newTestOutput()\n\tl := New(Config{Output: o}).(*logger)\n\tl.Warnf(\"test %d\", 1, F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"warn\", \"message\": \"test 1\", \"foo\": \"bar\"}, last)\n}\n\nfunc TestError(t *testing.T) {\n\to := newTestOutput()\n\tl := New(Config{Output: o}).(*logger)\n\tl.Error(\"test\", F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"error\", \"message\": \"test\", \"foo\": \"bar\"}, last)\n}\n\nfunc TestErrorf(t *testing.T) {\n\to := newTestOutput()\n\tl := New(Config{Output: o}).(*logger)\n\tl.Errorf(\"test %d%v\", 1, F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"error\", \"message\": \"test 1\", \"foo\": \"bar\"}, last)\n}\n\nfunc TestFatal(t *testing.T) {\n\te := exit1\n\texited := 0\n\texit1 = func() { exited++ }\n\tdefer func() { exit1 = e }()\n\to := newTestOutput()\n\tl := New(Config{Output: NewOutputChannel(o)}).(*logger)\n\tl.Fatal(\"test\", F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"fatal\", \"message\": \"test\", \"foo\": \"bar\"}, last)\n\tassert.Equal(t, 1, exited)\n}\n\nfunc TestFatalf(t *testing.T) {\n\te := exit1\n\texited := 0\n\texit1 = func() { exited++ }\n\tdefer func() { exit1 = e }()\n\to := newTestOutput()\n\tl := New(Config{Output: NewOutputChannel(o)}).(*logger)\n\tl.Fatalf(\"test %d%v\", 1, F{\"foo\": \"bar\"})\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"fatal\", \"message\": \"test 1\", \"foo\": \"bar\"}, last)\n\tassert.Equal(t, 1, exited)\n}\n\nfunc TestWrite(t *testing.T) {\n\to := newTestOutput()\n\txl := New(Config{Output: NewOutputChannel(o)}).(*logger)\n\tl := log.New(xl, \"prefix \", 0)\n\tl.Printf(\"test\")\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"info\", \"message\": \"prefix test\"}, last)\n}\n\nfunc TestOutput(t *testing.T) {\n\to := newTestOutput()\n\tl := New(Config{Output: o}).(*logger)\n\tl.Output(2, \"test\")\n\tlast := <-o.w\n\tassert.Contains(t, last[\"file\"], \"log_test.go:\")\n\tdelete(last, \"file\")\n\tassert.Equal(t, map[string]interface{}{\"time\": fakeNow, \"level\": \"info\", \"message\": \"test\"}, last)\n}\n"
  }
]