Full Code of rs/xlog for AI

master 131980fab91b cached
34 files
91.2 KB
26.5k tokens
233 symbols
1 requests
Download .txt
Repository: rs/xlog
Branch: master
Commit: 131980fab91b
Files: 34
Total size: 91.2 KB

Directory structure:
gitextract_0x8cvxva/

├── .travis.yml
├── LICENSE
├── README.md
├── handler.go
├── handler_examples_test.go
├── handler_pre17.go
├── handler_pre17_test.go
├── handler_test.go
├── internal/
│   └── term/
│       ├── LICENSE
│       ├── term.go
│       ├── term_appengine.go
│       ├── term_darwin.go
│       ├── term_freebsd.go
│       ├── term_linux.go
│       ├── term_notwindows.go
│       ├── term_openbsd.go
│       └── term_windows.go
├── levels.go
├── levels_test.go
├── nop.go
├── nop_test.go
├── output.go
├── output_examples_test.go
├── output_syslog.go
├── output_test.go
├── std.go
├── std_example_test.go
├── std_test.go
├── util.go
├── util_test.go
├── xlog.go
├── xlog_bench_test.go
├── xlog_examples_test.go
└── xlog_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .travis.yml
================================================
language: go
go:
- 1.7
- 1.8
- tip
matrix:
  allow_failures:
      - go: tip
script:
    go test -v -race -cpu=1,2,4 ./...


================================================
FILE: LICENSE
================================================
Copyright (c) 2015 Olivier Poitrey <rs@dailymotion.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
:warning: **Check [zerolog](https://github.com/rs/zerolog), the successor of xlog.**


# HTTP Handler Logger

[![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)

`xlog` is a logger for [net/context](https://godoc.org/golang.org/x/net/context) aware HTTP applications.

Unlike 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.

Read more about `xlog` on [Dailymotion engineering blog](http://engineering.dailymotion.com/our-way-to-go/).

![](screenshot.png)

## Features

- Per request log context
- Per request and/or per message key/value fields
- Log levels (Debug, Info, Warn, Error)
- Color output when terminal is detected
- Custom output (JSON, [logfmt](https://github.com/kr/logfmt), …)
- Automatic gathering of request context like User-Agent, IP etc.
- Drops message rather than blocking execution
- Easy access logging thru [github.com/rs/xaccess](https://github.com/rs/xaccess)

Works 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).

## Install

    go get github.com/rs/xlog

## Usage

```go
c := alice.New()

host, _ := os.Hostname()
conf := xlog.Config{
    // Log info level and higher
    Level: xlog.LevelInfo,
    // Set some global env fields
    Fields: xlog.F{
        "role": "my-service",
        "host": host,
    },
    // Output everything on console
    Output: xlog.NewOutputChannel(xlog.NewConsoleOutput()),
}

// Install the logger handler
c = c.Append(xlog.NewHandler(conf))

// Optionally plug the xlog handler's input to Go's default logger
log.SetFlags(0)
xlogger := xlog.New(conf)
log.SetOutput(xlogger)

// Install some provided extra handler to set some request's context fields.
// Thanks to those handler, all our logs will come with some pre-populated fields.
c = c.Append(xlog.MethodHandler("method"))
c = c.Append(xlog.URLHandler("url"))
c = c.Append(xlog.RemoteAddrHandler("ip"))
c = c.Append(xlog.UserAgentHandler("user_agent"))
c = c.Append(xlog.RefererHandler("referer"))
c = c.Append(xlog.RequestIDHandler("req_id", "Request-Id"))

// Here is your final handler
h := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // Get the logger from the request's context. You can safely assume it
    // will be always there: if the handler is removed, xlog.FromContext
    // will return a NopLogger
    l := xlog.FromRequest(r)

    // Then log some errors
    if err := errors.New("some error from elsewhere"); err != nil {
        l.Errorf("Here is an error: %v", err)
    }

    // Or some info with fields
    l.Info("Something happend", xlog.F{
        "user":   "current user id",
        "status": "ok",
    })
    // Output:
    // {
    //   "message": "Something happend",
    //   "level": "info",
    //   "file": "main.go:34",
    //   "time": time.Time{...},
    //   "user": "current user id",
    //   "status": "ok",
    //   "ip": "1.2.3.4",
    //   "user-agent": "Mozilla/1.2.3...",
    //   "referer": "http://somewhere.com/path",
    //   "role": "my-service",
    //   "host": "somehost"
    // }
}))
http.Handle("/", h)

if err := http.ListenAndServe(":8080", nil); err != nil {
    xlogger.Fatal(err)
}
```

### Copy Logger

You may want to get a copy of the current logger to pass a modified version to a function without touching the original:

```go
l := xlog.FromContext(ctx)
l2 := xlog.Copy(l)
l2.SetField("foo", "bar")
```

Make 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:

```go
l := xlog.FromContext(ctx)
l2 := xlog.Copy(l)
go func() {
    // use the safe copy
    l2.Info("something")
}()
```

### Global Logger

You may use the standard Go logger and plug `xlog` as it's output as `xlog` implements `io.Writer`:

```go
xlogger := xlog.New(conf)
log.SetOutput(xlogger)
```

This 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.

Another option for code you manage but which is outside of a HTTP request handler is to use the `xlog` provided default logger:

```go
xlog.Debugf("some message with %s", variable, xlog.F{"and": "field support"})
```

This 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:

```go
xlog.SetLogger(xlog.New(xlog.Config{
    Level: xlog.LevelInfo,
    Output: xlog.NewConsoleOutput(),
    Fields: xlog.F{
        "role": "my-service",
    },
}))
```

### Configure Output

By default, output is setup to output debug and info message on `STDOUT` and warning and errors to `STDERR`. You can easily change this setup.

XLog 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.

```go
conf := xlog.Config{
    Output: xlog.NewOutputChannel(xlog.MultiOutput{
        // Send all logs with field type=mymodule to a remote syslog
        0: xlog.FilterOutput{
            Cond: func(fields map[string]interface{}) bool {
                return fields["type"] == "mymodule"
            },
            Output: xlog.NewSyslogOutput("tcp", "1.2.3.4:1234", "mymodule"),
        },
        // Setup different output per log level
        1: xlog.LevelOutput{
            // Send errors to the console
            Error: xlog.NewConsoleOutput(),
            // Send syslog output for error level
            Info: xlog.NewSyslogOutput("", "", ""),
        },
    }),
})

h = xlog.NewHandler(conf)
```

#### Built-in Output Modules

| Name | Description |
|------|-------------|
| [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.
| [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.
| [FilterOutput](https://godoc.org/github.com/rs/xlog#FilterOutput) | Tests a condition on the message and forward it to the child output if true.
| [LevelOutput](https://godoc.org/github.com/rs/xlog#LevelOutput) | Routes messages per level outputs.
| [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.
| [JSONOutput](https://godoc.org/github.com/rs/xlog#NewJSONOutput) | Serialize messages in JSON.
| [LogfmtOutput](https://godoc.org/github.com/rs/xlog#NewLogfmtOutput) | Serialize messages using Heroku like [logfmt](https://github.com/kr/logfmt).
| [LogstashOutput](https://godoc.org/github.com/rs/xlog#NewLogstashOutput) | Serialize JSON message using Logstash 2.0 (schema v1) structured format.
| [SyslogOutput](https://godoc.org/github.com/rs/xlog#NewSyslogOutput) | Send messages to syslog.
| [UIDOutput](https://godoc.org/github.com/rs/xlog#NewUIDOutput) | Append a globally unique id to every message and forward it to the next output.

## Third Party Extensions

| Project | Author | Description |
|---------|--------|-------------|
| [gRPClog](https://github.com/clawio/grpcxlog) | [Hugo González Labrador](https://github.com/labkode) | An adapter to use xlog as the logger for grpclog.
| [xlog-nsq](https://github.com/rs/xlog-nsq) | [Olivier Poitrey](https://github.com/rs) | An xlog to [NSQ](http://nsq.io) output.
| [xlog-sentry](https://github.com/trong/xlog-sentry) | [trong](https://github.com/trong) | An xlog to [Sentry](https://getsentry.com/) output.

## Licenses

All source code is licensed under the [MIT License](https://raw.github.com/rs/xlog/master/LICENSE).


================================================
FILE: handler.go
================================================
// +build go1.7

package xlog

import (
	"context"
	"net"
	"net/http"

	"github.com/rs/xid"
)

type key int

const (
	logKey key = iota
	idKey
)

// IDFromContext returns the unique id associated to the request if any.
func IDFromContext(ctx context.Context) (xid.ID, bool) {
	id, ok := ctx.Value(idKey).(xid.ID)
	return id, ok
}

// IDFromRequest returns the unique id accociated to the request if any.
func IDFromRequest(r *http.Request) (xid.ID, bool) {
	if r == nil {
		return xid.ID{}, false
	}
	return IDFromContext(r.Context())
}

// FromContext gets the logger out of the context.
// If not logger is stored in the context, a NopLogger is returned.
func FromContext(ctx context.Context) Logger {
	if ctx == nil {
		return NopLogger
	}
	l, ok := ctx.Value(logKey).(Logger)
	if !ok {
		return NopLogger
	}
	return l
}

// FromRequest gets the logger in the request's context.
// This is a shortcut for xlog.FromContext(r.Context())
func FromRequest(r *http.Request) Logger {
	if r == nil {
		return NopLogger
	}
	return FromContext(r.Context())
}

// NewContext returns a copy of the parent context and associates it with the provided logger.
func NewContext(ctx context.Context, l Logger) context.Context {
	return context.WithValue(ctx, logKey, l)
}

// NewHandler instanciates a new xlog HTTP handler.
//
// If not configured, the output is set to NewConsoleOutput() by default.
func NewHandler(c Config) func(http.Handler) http.Handler {
	if c.Output == nil {
		c.Output = NewOutputChannel(NewConsoleOutput())
	}
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			var l Logger
			if r != nil {
				l = New(c)
				r = r.WithContext(NewContext(r.Context(), l))
			}
			next.ServeHTTP(w, r)
			if l, ok := l.(*logger); ok {
				l.close()
			}
		})
	}
}

// URLHandler returns a handler setting the request's URL as a field
// to the current context's logger using the passed name as field name.
func URLHandler(name string) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			l := FromContext(r.Context())
			l.SetField(name, r.URL.String())
			next.ServeHTTP(w, r)
		})
	}
}

// MethodHandler returns a handler setting the request's method as a field
// to the current context's logger using the passed name as field name.
func MethodHandler(name string) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			l := FromContext(r.Context())
			l.SetField(name, r.Method)
			next.ServeHTTP(w, r)
		})
	}
}

// RequestHandler returns a handler setting the request's method and URL as a field
// to the current context's logger using the passed name as field name.
func RequestHandler(name string) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			l := FromContext(r.Context())
			l.SetField(name, r.Method+" "+r.URL.String())
			next.ServeHTTP(w, r)
		})
	}
}

// RemoteAddrHandler returns a handler setting the request's remote address as a field
// to the current context's logger using the passed name as field name.
func RemoteAddrHandler(name string) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
				l := FromContext(r.Context())
				l.SetField(name, host)
			}
			next.ServeHTTP(w, r)
		})
	}
}

// UserAgentHandler returns a handler setting the request's client's user-agent as
// a field to the current context's logger using the passed name as field name.
func UserAgentHandler(name string) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if ua := r.Header.Get("User-Agent"); ua != "" {
				l := FromContext(r.Context())
				l.SetField(name, ua)
			}
			next.ServeHTTP(w, r)
		})
	}
}

// RefererHandler returns a handler setting the request's referer header as
// a field to the current context's logger using the passed name as field name.
func RefererHandler(name string) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if ref := r.Header.Get("Referer"); ref != "" {
				l := FromContext(r.Context())
				l.SetField(name, ref)
			}
			next.ServeHTTP(w, r)
		})
	}
}

// RequestIDHandler returns a handler setting a unique id to the request which can
// be gathered using IDFromContext(ctx). This generated id is added as a field to the
// logger using the passed name as field name. The id is also added as a response
// header if the headerName is not empty.
//
// The generated id is a URL safe base64 encoded mongo object-id-like unique id.
// Mongo unique id generation algorithm has been selected as a trade-off between
// size and ease of use: UUID is less space efficient and snowflake requires machine
// configuration.
func RequestIDHandler(name, headerName string) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ctx := r.Context()
			id, ok := IDFromContext(ctx)
			if !ok {
				id = xid.New()
				ctx = context.WithValue(ctx, idKey, id)
				r = r.WithContext(ctx)
			}
			if name != "" {
				FromContext(ctx).SetField(name, id)
			}
			if headerName != "" {
				w.Header().Set(headerName, id.String())
			}
			next.ServeHTTP(w, r)
		})
	}
}


================================================
FILE: handler_examples_test.go
================================================
// +build go1.7

package xlog_test

import (
	"errors"
	"log"
	"net/http"
	"os"

	"github.com/justinas/alice"
	"github.com/rs/xlog"
)

func Example_handler() {
	c := alice.New()

	host, _ := os.Hostname()
	conf := xlog.Config{
		// Set some global env fields
		Fields: xlog.F{
			"role": "my-service",
			"host": host,
		},
	}

	// Install the logger handler with default output on the console
	c = c.Append(xlog.NewHandler(conf))

	// Plug the xlog handler's input to Go's default logger
	log.SetFlags(0)
	log.SetOutput(xlog.New(conf))

	// Install some provided extra handler to set some request's context fields.
	// Thanks to those handler, all our logs will come with some pre-populated fields.
	c = c.Append(xlog.RemoteAddrHandler("ip"))
	c = c.Append(xlog.UserAgentHandler("user_agent"))
	c = c.Append(xlog.RefererHandler("referer"))
	c = c.Append(xlog.RequestIDHandler("req_id", "Request-Id"))

	// Here is your final handler
	h := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Get the logger from the request's context. You can safely assume it
		// will be always there: if the handler is removed, xlog.FromContext
		// will return a NopLogger
		l := xlog.FromRequest(r)

		// Then log some errors
		if err := errors.New("some error from elsewhere"); err != nil {
			l.Errorf("Here is an error: %v", err)
		}

		// Or some info with fields
		l.Info("Something happend", xlog.F{
			"user":   "current user id",
			"status": "ok",
		})
	}))
	http.Handle("/", h)

	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.SetOutput(os.Stderr) // make sure we print to console
		log.Fatal(err)
	}
}


================================================
FILE: handler_pre17.go
================================================
// +build !go1.7

package xlog

import (
	"net"
	"net/http"

	"github.com/rs/xhandler"
	"github.com/rs/xid"
	"golang.org/x/net/context"
)

type key int

const (
	logKey key = iota
	idKey
)

// IDFromContext returns the unique id associated to the request if any.
func IDFromContext(ctx context.Context) (xid.ID, bool) {
	id, ok := ctx.Value(idKey).(xid.ID)
	return id, ok
}

// FromContext gets the logger out of the context.
// If not logger is stored in the context, a NopLogger is returned.
func FromContext(ctx context.Context) Logger {
	if ctx == nil {
		return NopLogger
	}
	l, ok := ctx.Value(logKey).(Logger)
	if !ok {
		return NopLogger
	}
	return l
}

// NewContext returns a copy of the parent context and associates it with the provided logger.
func NewContext(ctx context.Context, l Logger) context.Context {
	return context.WithValue(ctx, logKey, l)
}

// NewHandler instanciates a new xlog HTTP handler.
//
// If not configured, the output is set to NewConsoleOutput() by default.
func NewHandler(c Config) func(xhandler.HandlerC) xhandler.HandlerC {
	if c.Output == nil {
		c.Output = NewOutputChannel(NewConsoleOutput())
	}
	return func(next xhandler.HandlerC) xhandler.HandlerC {
		return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			l := New(c)
			ctx = NewContext(ctx, l)
			next.ServeHTTPC(ctx, w, r)
			if l, ok := l.(*logger); ok {
				l.close()
			}
		})
	}
}

// URLHandler returns a handler setting the request's URL as a field
// to the current context's logger using the passed name as field name.
func URLHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {
	return func(next xhandler.HandlerC) xhandler.HandlerC {
		return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			FromContext(ctx).SetField(name, r.URL.String())
			next.ServeHTTPC(ctx, w, r)
		})
	}
}

// MethodHandler returns a handler setting the request's method as a field
// to the current context's logger using the passed name as field name.
func MethodHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {
	return func(next xhandler.HandlerC) xhandler.HandlerC {
		return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			FromContext(ctx).SetField(name, r.Method)
			next.ServeHTTPC(ctx, w, r)
		})
	}
}

// RequestHandler returns a handler setting the request's method and URL as a field
// to the current context's logger using the passed name as field name.
func RequestHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {
	return func(next xhandler.HandlerC) xhandler.HandlerC {
		return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			FromContext(ctx).SetField(name, r.Method+" "+r.URL.String())
			next.ServeHTTPC(ctx, w, r)
		})
	}
}

// RemoteAddrHandler returns a handler setting the request's remote address as a field
// to the current context's logger using the passed name as field name.
func RemoteAddrHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {
	return func(next xhandler.HandlerC) xhandler.HandlerC {
		return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
				FromContext(ctx).SetField(name, host)
			}
			next.ServeHTTPC(ctx, w, r)
		})
	}
}

// UserAgentHandler returns a handler setting the request's client's user-agent as
// a field to the current context's logger using the passed name as field name.
func UserAgentHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {
	return func(next xhandler.HandlerC) xhandler.HandlerC {
		return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			if ua := r.Header.Get("User-Agent"); ua != "" {
				FromContext(ctx).SetField(name, ua)
			}
			next.ServeHTTPC(ctx, w, r)
		})
	}
}

// RefererHandler returns a handler setting the request's referer header as
// a field to the current context's logger using the passed name as field name.
func RefererHandler(name string) func(next xhandler.HandlerC) xhandler.HandlerC {
	return func(next xhandler.HandlerC) xhandler.HandlerC {
		return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			if ref := r.Header.Get("Referer"); ref != "" {
				FromContext(ctx).SetField(name, ref)
			}
			next.ServeHTTPC(ctx, w, r)
		})
	}
}

// RequestIDHandler returns a handler setting a unique id to the request which can
// be gathered using IDFromContext(ctx). This generated id is added as a field to the
// logger using the passed name as field name. The id is also added as a response
// header if the headerName is not empty.
//
// The generated id is a URL safe base64 encoded mongo object-id-like unique id.
// Mongo unique id generation algorithm has been selected as a trade-off between
// size and ease of use: UUID is less space efficient and snowflake requires machine
// configuration.
func RequestIDHandler(name, headerName string) func(next xhandler.HandlerC) xhandler.HandlerC {
	return func(next xhandler.HandlerC) xhandler.HandlerC {
		return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
			id, ok := IDFromContext(ctx)
			if !ok {
				id = xid.New()
				ctx = context.WithValue(ctx, idKey, id)
			}
			if name != "" {
				FromContext(ctx).SetField(name, id)
			}
			if headerName != "" {
				w.Header().Set(headerName, id.String())
			}
			next.ServeHTTPC(ctx, w, r)
		})
	}
}


================================================
FILE: handler_pre17_test.go
================================================
// +build !go1.7

package xlog

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/rs/xhandler"
	"github.com/stretchr/testify/assert"
	"golang.org/x/net/context"
)

func TestFromContext(t *testing.T) {
	assert.Equal(t, NopLogger, FromContext(nil))
	assert.Equal(t, NopLogger, FromContext(context.Background()))
	l := &logger{}
	ctx := NewContext(context.Background(), l)
	assert.Equal(t, l, FromContext(ctx))
}

func TestNewHandler(t *testing.T) {
	c := Config{
		Level:  LevelInfo,
		Fields: F{"foo": "bar"},
		Output: NewOutputChannel(&testOutput{}),
	}
	lh := NewHandler(c)
	h := lh(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		l := FromContext(ctx)
		assert.NotNil(t, l)
		assert.NotEqual(t, NopLogger, l)
		if l, ok := l.(*logger); assert.True(t, ok) {
			assert.Equal(t, LevelInfo, l.level)
			assert.Equal(t, c.Output, l.output)
			assert.Equal(t, F{"foo": "bar"}, F(l.fields))
		}
	}))
	h.ServeHTTPC(context.Background(), nil, nil)
}

func TestURLHandler(t *testing.T) {
	r := &http.Request{
		URL: &url.URL{Path: "/path", RawQuery: "foo=bar"},
	}
	h := URLHandler("url")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		l := FromContext(ctx).(*logger)
		assert.Equal(t, F{"url": "/path?foo=bar"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTPC(context.Background(), nil, r)
}

func TestMethodHandler(t *testing.T) {
	r := &http.Request{
		Method: "POST",
	}
	h := MethodHandler("method")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		l := FromContext(ctx).(*logger)
		assert.Equal(t, F{"method": "POST"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTPC(context.Background(), nil, r)
}

func TestRequestHandler(t *testing.T) {
	r := &http.Request{
		Method: "POST",
		URL:    &url.URL{Path: "/path", RawQuery: "foo=bar"},
	}
	h := RequestHandler("request")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		l := FromContext(ctx).(*logger)
		assert.Equal(t, F{"request": "POST /path?foo=bar"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTPC(context.Background(), nil, r)
}

func TestRemoteAddrHandler(t *testing.T) {
	r := &http.Request{
		RemoteAddr: "1.2.3.4:1234",
	}
	h := RemoteAddrHandler("ip")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		l := FromContext(ctx).(*logger)
		assert.Equal(t, F{"ip": "1.2.3.4"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTPC(context.Background(), nil, r)
}

func TestRemoteAddrHandlerIPv6(t *testing.T) {
	r := &http.Request{
		RemoteAddr: "[2001:db8:a0b:12f0::1]:1234",
	}
	h := RemoteAddrHandler("ip")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		l := FromContext(ctx).(*logger)
		assert.Equal(t, F{"ip": "2001:db8:a0b:12f0::1"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTPC(context.Background(), nil, r)
}

func TestUserAgentHandler(t *testing.T) {
	r := &http.Request{
		Header: http.Header{
			"User-Agent": []string{"some user agent string"},
		},
	}
	h := UserAgentHandler("ua")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		l := FromContext(ctx).(*logger)
		assert.Equal(t, F{"ua": "some user agent string"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTPC(context.Background(), nil, r)
}

func TestRefererHandler(t *testing.T) {
	r := &http.Request{
		Header: http.Header{
			"Referer": []string{"http://foo.com/bar"},
		},
	}
	h := RefererHandler("ua")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		l := FromContext(ctx).(*logger)
		assert.Equal(t, F{"ua": "http://foo.com/bar"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTPC(context.Background(), nil, r)
}

func TestRequestIDHandler(t *testing.T) {
	r := &http.Request{}
	h := RequestIDHandler("id", "Request-Id")(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		l := FromContext(ctx).(*logger)
		if id, ok := IDFromContext(ctx); assert.True(t, ok) {
			assert.Equal(t, l.fields["id"], id)
			assert.Len(t, id.String(), 20)
			assert.Equal(t, id.String(), w.Header().Get("Request-Id"))
		}
		assert.Len(t, l.fields["id"], 12)
	}))
	h = NewHandler(Config{})(h)
	w := httptest.NewRecorder()
	h.ServeHTTPC(context.Background(), w, r)
}


================================================
FILE: handler_test.go
================================================
// +build go1.7

package xlog

import (
	"context"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestFromContext(t *testing.T) {
	assert.Equal(t, NopLogger, FromContext(nil))
	assert.Equal(t, NopLogger, FromContext(context.Background()))
	l := &logger{}
	ctx := NewContext(context.Background(), l)
	assert.Equal(t, l, FromContext(ctx))
}

func TestNewHandler(t *testing.T) {
	c := Config{
		Level:  LevelInfo,
		Fields: F{"foo": "bar"},
		Output: NewOutputChannel(&testOutput{}),
	}
	lh := NewHandler(c)
	h := lh(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		l := FromRequest(r)
		assert.NotNil(t, l)
		assert.NotEqual(t, NopLogger, l)
		if l, ok := l.(*logger); assert.True(t, ok) {
			assert.Equal(t, LevelInfo, l.level)
			assert.Equal(t, c.Output, l.output)
			assert.Equal(t, F{"foo": "bar"}, F(l.fields))
		}
	}))
	h.ServeHTTP(nil, &http.Request{})
}

func TestURLHandler(t *testing.T) {
	r := &http.Request{
		URL: &url.URL{Path: "/path", RawQuery: "foo=bar"},
	}
	h := URLHandler("url")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		l := FromRequest(r).(*logger)
		assert.Equal(t, F{"url": "/path?foo=bar"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTP(nil, r)
}

func TestMethodHandler(t *testing.T) {
	r := &http.Request{
		Method: "POST",
	}
	h := MethodHandler("method")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		l := FromRequest(r).(*logger)
		assert.Equal(t, F{"method": "POST"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTP(nil, r)
}

func TestRequestHandler(t *testing.T) {
	r := &http.Request{
		Method: "POST",
		URL:    &url.URL{Path: "/path", RawQuery: "foo=bar"},
	}
	h := RequestHandler("request")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		l := FromRequest(r).(*logger)
		assert.Equal(t, F{"request": "POST /path?foo=bar"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTP(nil, r)
}

func TestRemoteAddrHandler(t *testing.T) {
	r := &http.Request{
		RemoteAddr: "1.2.3.4:1234",
	}
	h := RemoteAddrHandler("ip")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		l := FromRequest(r).(*logger)
		assert.Equal(t, F{"ip": "1.2.3.4"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTP(nil, r)
}

func TestRemoteAddrHandlerIPv6(t *testing.T) {
	r := &http.Request{
		RemoteAddr: "[2001:db8:a0b:12f0::1]:1234",
	}
	h := RemoteAddrHandler("ip")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		l := FromRequest(r).(*logger)
		assert.Equal(t, F{"ip": "2001:db8:a0b:12f0::1"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTP(nil, r)
}

func TestUserAgentHandler(t *testing.T) {
	r := &http.Request{
		Header: http.Header{
			"User-Agent": []string{"some user agent string"},
		},
	}
	h := UserAgentHandler("ua")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		l := FromRequest(r).(*logger)
		assert.Equal(t, F{"ua": "some user agent string"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTP(nil, r)
}

func TestRefererHandler(t *testing.T) {
	r := &http.Request{
		Header: http.Header{
			"Referer": []string{"http://foo.com/bar"},
		},
	}
	h := RefererHandler("ua")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		l := FromRequest(r).(*logger)
		assert.Equal(t, F{"ua": "http://foo.com/bar"}, F(l.fields))
	}))
	h = NewHandler(Config{})(h)
	h.ServeHTTP(nil, r)
}

func TestRequestIDHandler(t *testing.T) {
	r := &http.Request{}
	h := RequestIDHandler("id", "Request-Id")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		l := FromRequest(r).(*logger)
		if id, ok := IDFromRequest(r); assert.True(t, ok) {
			assert.Equal(t, l.fields["id"], id)
			assert.Len(t, id.String(), 20)
			assert.Equal(t, id.String(), w.Header().Get("Request-Id"))
		}
		assert.Len(t, l.fields["id"], 12)
	}))
	h = NewHandler(Config{})(h)
	w := httptest.NewRecorder()
	h.ServeHTTP(w, r)
}


================================================
FILE: internal/term/LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014 Simon Eskildsen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: internal/term/term.go
================================================
// Based on ssh/terminal:
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build linux,!appengine darwin freebsd openbsd

package term

type fder interface {
	Fd() uintptr
}


================================================
FILE: internal/term/term_appengine.go
================================================
// Based on ssh/terminal:
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build appengine

package term

import "io"

// IsTerminal always returns false on AppEngine.
func IsTerminal(w io.Writer) bool {
	return false
}


================================================
FILE: internal/term/term_darwin.go
================================================
// Based on ssh/terminal:
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package term

import "syscall"

const ioctlReadTermios = syscall.TIOCGETA


================================================
FILE: internal/term/term_freebsd.go
================================================
package term

import (
	"syscall"
)

const ioctlReadTermios = syscall.TIOCGETA


================================================
FILE: internal/term/term_linux.go
================================================
// Based on ssh/terminal:
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !appengine

package term

import "syscall"

const ioctlReadTermios = syscall.TCGETS


================================================
FILE: internal/term/term_notwindows.go
================================================
// Based on ssh/terminal:
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build linux,!appengine darwin freebsd openbsd

package term

import (
	"io"
	"syscall"
	"unsafe"
)

// IsTerminal returns true if w writes to a terminal.
func IsTerminal(w io.Writer) bool {
	fw, ok := w.(fder)
	if !ok {
		return false
	}
	var termios syscall.Termios
	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fw.Fd(), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
	return err == 0
}


================================================
FILE: internal/term/term_openbsd.go
================================================
package term

import "syscall"

const ioctlReadTermios = syscall.TIOCGETA


================================================
FILE: internal/term/term_windows.go
================================================
// Based on ssh/terminal:
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

package term

import (
	"io"
	"syscall"
	"unsafe"
)

var kernel32 = syscall.NewLazyDLL("kernel32.dll")

var (
	procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
)

// IsTerminal returns true if w writes to a terminal.
func IsTerminal(w io.Writer) bool {
	fw, ok := w.(interface {
		Fd() uintptr
	})
	if !ok {
		return false
	}
	var st uint32
	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fw.Fd(), uintptr(unsafe.Pointer(&st)), 0)
	return r != 0 && e == 0
}


================================================
FILE: levels.go
================================================
package xlog

import (
	"bytes"
	"fmt"
	"strconv"
)

// Level defines log levels
type Level int

// Log levels
const (
	LevelDebug Level = iota
	LevelInfo
	LevelWarn
	LevelError
	LevelFatal
)

// Log level strings
var (
	levelDebug = "debug"
	levelInfo  = "info"
	levelWarn  = "warn"
	levelError = "error"
	levelFatal = "fatal"

	levelBytesDebug = []byte(levelDebug)
	levelBytesInfo  = []byte(levelInfo)
	levelBytesWarn  = []byte(levelWarn)
	levelBytesError = []byte(levelError)
	levelBytesFatal = []byte(levelFatal)
)

// LevelFromString returns the level based on its string representation
func LevelFromString(t string) (Level, error) {
	l := Level(0)
	err := (&l).UnmarshalText([]byte(t))
	return l, err
}

// UnmarshalText lets Level implements the TextUnmarshaler interface used by encoding packages
func (l *Level) UnmarshalText(text []byte) (err error) {
	if bytes.Equal(text, levelBytesDebug) {
		*l = LevelDebug
	} else if bytes.Equal(text, levelBytesInfo) {
		*l = LevelInfo
	} else if bytes.Equal(text, levelBytesWarn) {
		*l = LevelWarn
	} else if bytes.Equal(text, levelBytesError) {
		*l = LevelError
	} else if bytes.Equal(text, levelBytesFatal) {
		*l = LevelFatal
	} else {
		err = fmt.Errorf("Uknown level %v", string(text))
	}
	return
}

// String returns the string representation of the level.
func (l Level) String() string {
	var t string
	switch l {
	case LevelDebug:
		t = levelDebug
	case LevelInfo:
		t = levelInfo
	case LevelWarn:
		t = levelWarn
	case LevelError:
		t = levelError
	case LevelFatal:
		t = levelFatal
	default:
		t = strconv.FormatInt(int64(l), 10)
	}
	return t
}

// MarshalText lets Level implements the TextMarshaler interface used by encoding packages
func (l Level) MarshalText() ([]byte, error) {
	var t []byte
	switch l {
	case LevelDebug:
		t = levelBytesDebug
	case LevelInfo:
		t = levelBytesInfo
	case LevelWarn:
		t = levelBytesWarn
	case LevelError:
		t = levelBytesError
	case LevelFatal:
		t = levelBytesFatal
	default:
		t = []byte(strconv.FormatInt(int64(l), 10))
	}
	return t, nil
}


================================================
FILE: levels_test.go
================================================
package xlog

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestLevelFromString(t *testing.T) {
	l, err := LevelFromString("debug")
	assert.NoError(t, err)
	assert.Equal(t, LevelDebug, l)
	l, err = LevelFromString("info")
	assert.NoError(t, err)
	assert.Equal(t, LevelInfo, l)
	l, err = LevelFromString("warn")
	assert.NoError(t, err)
	assert.Equal(t, LevelWarn, l)
	l, err = LevelFromString("error")
	assert.NoError(t, err)
	assert.Equal(t, LevelError, l)
	l, err = LevelFromString("fatal")
	assert.NoError(t, err)
	assert.Equal(t, LevelFatal, l)
	_, err = LevelFromString("foo")
	assert.Error(t, err, "")
}

func TestLevelUnmarshalerText(t *testing.T) {
	l := Level(-1)
	err := l.UnmarshalText([]byte("debug"))
	assert.NoError(t, err)
	assert.Equal(t, LevelDebug, l)
	err = l.UnmarshalText([]byte("info"))
	assert.NoError(t, err)
	assert.Equal(t, LevelInfo, l)
	err = l.UnmarshalText([]byte("warn"))
	assert.NoError(t, err)
	assert.Equal(t, LevelWarn, l)
	err = l.UnmarshalText([]byte("error"))
	assert.NoError(t, err)
	assert.Equal(t, LevelError, l)
	err = l.UnmarshalText([]byte("fatal"))
	assert.NoError(t, err)
	assert.Equal(t, LevelFatal, l)
	assert.Error(t, l.UnmarshalText([]byte("invalid")))
}

func TestLevelString(t *testing.T) {
	assert.Equal(t, "debug", LevelDebug.String())
	assert.Equal(t, "info", LevelInfo.String())
	assert.Equal(t, "warn", LevelWarn.String())
	assert.Equal(t, "error", LevelError.String())
	assert.Equal(t, "fatal", LevelFatal.String())
	assert.Equal(t, "10", Level(10).String())
}

func TestLevelMarshalerText(t *testing.T) {
	b, err := LevelDebug.MarshalText()
	assert.NoError(t, err)
	assert.Equal(t, string(levelBytesDebug), string(b))
	b, err = LevelInfo.MarshalText()
	assert.NoError(t, err)
	assert.Equal(t, string(levelBytesInfo), string(b))
	b, err = LevelWarn.MarshalText()
	assert.NoError(t, err)
	assert.Equal(t, string(levelBytesWarn), string(b))
	b, err = LevelError.MarshalText()
	assert.NoError(t, err)
	assert.Equal(t, string(levelBytesError), string(b))
	b, err = LevelFatal.MarshalText()
	assert.NoError(t, err)
	assert.Equal(t, string(levelBytesFatal), string(b))
	b, err = Level(10).MarshalText()
	assert.NoError(t, err)
	assert.Equal(t, "10", string(b))
}


================================================
FILE: nop.go
================================================
package xlog

type nop struct{}

// NopLogger is an no-op implementation of xlog.Logger
var NopLogger = &nop{}

func (n nop) SetField(name string, value interface{}) {}

func (n nop) GetFields() F { return map[string]interface{}{} }

func (n nop) OutputF(level Level, calldepth int, msg string, fields map[string]interface{}) {}

func (n nop) Debug(v ...interface{}) {}

func (n nop) Debugf(format string, v ...interface{}) {}

func (n nop) Info(v ...interface{}) {}

func (n nop) Infof(format string, v ...interface{}) {}

func (n nop) Warn(v ...interface{}) {}

func (n nop) Warnf(format string, v ...interface{}) {}

func (n nop) Error(v ...interface{}) {}

func (n nop) Errorf(format string, v ...interface{}) {}

func (n nop) Fatal(v ...interface{}) {
	exit1()
}

func (n nop) Fatalf(format string, v ...interface{}) {
	exit1()
}

func (n nop) Write(p []byte) (int, error) { return len(p), nil }

func (n nop) Output(calldepth int, s string) error {
	return nil
}


================================================
FILE: nop_test.go
================================================
package xlog

import "testing"

func TestNopLogger(t *testing.T) {
	// cheap cover score upper
	NopLogger.SetField("name", "value")
	NopLogger.OutputF(LevelInfo, 0, "", nil)
	NopLogger.Debug()
	NopLogger.Debugf("format")
	NopLogger.Info()
	NopLogger.Infof("format")
	NopLogger.Warn()
	NopLogger.Warnf("format")
	NopLogger.Error()
	NopLogger.Errorf("format")
	exit1 = func() {}
	NopLogger.Fatal()
	NopLogger.Fatalf("format")
	NopLogger.Write([]byte{})
	NopLogger.Output(0, "")
}


================================================
FILE: output.go
================================================
package xlog

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"os"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/rs/xid"
	"github.com/rs/xlog/internal/term"
)

// Output sends a log message fields to a destination.
type Output interface {
	Write(fields map[string]interface{}) error
}

// OutputFunc is an adapter to allow the use of ordinary functions as Output handlers.
// If it is a function with the appropriate signature, OutputFunc(f) is a Output object
// that calls f on Write().
type OutputFunc func(fields map[string]interface{}) error

func (of OutputFunc) Write(fields map[string]interface{}) error {
	return of(fields)
}

// OutputChannel is a send buffered channel between xlog and an Output.
type OutputChannel struct {
	input  chan map[string]interface{}
	output Output
	stop   chan struct{}
}

// ErrBufferFull is returned when the output channel buffer is full and messages
// are discarded.
var ErrBufferFull = errors.New("buffer full")

// NewOutputChannel creates a consumer buffered channel for the given output
// with a default buffer of 100 messages.
func NewOutputChannel(o Output) *OutputChannel {
	return NewOutputChannelBuffer(o, 100)
}

// NewOutputChannelBuffer creates a consumer buffered channel for the given output
// with a customizable buffer size.
func NewOutputChannelBuffer(o Output, bufSize int) *OutputChannel {
	oc := &OutputChannel{
		input:  make(chan map[string]interface{}, bufSize),
		output: o,
		stop:   make(chan struct{}),
	}

	go func() {
		for {
			select {
			case msg := <-oc.input:
				if err := o.Write(msg); err != nil {
					critialLogger.Print("cannot write log message: ", err.Error())
				}
			case <-oc.stop:
				close(oc.stop)
				return
			}
		}
	}()

	return oc
}

// Write implements the Output interface
func (oc *OutputChannel) Write(fields map[string]interface{}) (err error) {
	select {
	case oc.input <- fields:
		// Sent with success
	default:
		// Channel is full, message dropped
		err = ErrBufferFull
	}
	return err
}

// Flush flushes all the buffered message to the output
func (oc *OutputChannel) Flush() {
	for {
		select {
		case msg := <-oc.input:
			if err := oc.output.Write(msg); err != nil {
				critialLogger.Print("cannot write log message: ", err.Error())
			}
		default:
			return
		}
	}
}

// Close closes the output channel and release the consumer's go routine.
func (oc *OutputChannel) Close() {
	if oc.stop == nil {
		return
	}
	oc.stop <- struct{}{}
	<-oc.stop
	oc.stop = nil
	oc.Flush()
}

// Discard is an Output that discards all log message going thru it.
var Discard = OutputFunc(func(fields map[string]interface{}) error {
	return nil
})

var bufPool = &sync.Pool{
	New: func() interface{} {
		return &bytes.Buffer{}
	},
}

// MultiOutput routes the same message to serveral outputs.
// If one or more outputs return an error, the last error is returned.
type MultiOutput []Output

func (m MultiOutput) Write(fields map[string]interface{}) (err error) {
	for _, o := range m {
		e := o.Write(fields)
		if e != nil {
			err = e
		}
	}
	return
}

// FilterOutput test a condition on the message and forward it to the child output
// if it returns true.
type FilterOutput struct {
	Cond   func(fields map[string]interface{}) bool
	Output Output
}

func (f FilterOutput) Write(fields map[string]interface{}) (err error) {
	if f.Output == nil {
		return
	}
	if f.Cond(fields) {
		return f.Output.Write(fields)
	}
	return
}

// LevelOutput routes messages to different output based on the message's level.
type LevelOutput struct {
	Debug Output
	Info  Output
	Warn  Output
	Error Output
	Fatal Output
}

func (l LevelOutput) Write(fields map[string]interface{}) error {
	var o Output
	switch fields[KeyLevel] {
	case "debug":
		o = l.Debug
	case "info":
		o = l.Info
	case "warn":
		o = l.Warn
	case "error":
		o = l.Error
	case "fatal":
		o = l.Fatal
	}
	if o != nil {
		return o.Write(fields)
	}
	return nil
}

// RecorderOutput stores the raw messages in it's Messages field. This output is useful for testing.
type RecorderOutput struct {
	Messages []F
}

func (l *RecorderOutput) Write(fields map[string]interface{}) error {
	if l.Messages == nil {
		l.Messages = []F{fields}
	} else {
		l.Messages = append(l.Messages, fields)
	}
	return nil
}

// Reset empty the output from stored messages
func (l *RecorderOutput) Reset() {
	l.Messages = []F{}
}

type consoleOutput struct {
	w io.Writer
}

var isTerminal = term.IsTerminal

// NewConsoleOutput returns a Output printing message in a colored human readable form on the
// stderr. If the stderr is not on a terminal, a LogfmtOutput is returned instead.
func NewConsoleOutput() Output {
	return NewConsoleOutputW(os.Stderr, NewLogfmtOutput(os.Stderr))
}

// NewConsoleOutputW returns a Output printing message in a colored human readable form with
// the provided writer. If the writer is not on a terminal, the noTerm output is returned.
func NewConsoleOutputW(w io.Writer, noTerm Output) Output {
	if isTerminal(w) {
		return consoleOutput{w: w}
	}
	return noTerm
}

func (o consoleOutput) Write(fields map[string]interface{}) error {
	buf := bufPool.Get().(*bytes.Buffer)
	defer func() {
		buf.Reset()
		bufPool.Put(buf)
	}()
	if ts, ok := fields[KeyTime].(time.Time); ok {
		buf.Write([]byte(ts.Format("2006/01/02 15:04:05 ")))
	}
	if lvl, ok := fields[KeyLevel].(string); ok {
		levelColor := blue
		switch lvl {
		case "debug":
			levelColor = gray
		case "warn":
			levelColor = yellow
		case "error":
			levelColor = red
		}
		colorPrint(buf, strings.ToUpper(lvl[0:4]), levelColor)
		buf.WriteByte(' ')
	}
	if msg, ok := fields[KeyMessage].(string); ok {
		msg = strings.Replace(msg, "\n", "\\n", -1)
		buf.Write([]byte(msg))
	}
	// Gather field keys
	keys := []string{}
	for k := range fields {
		switch k {
		case KeyLevel, KeyMessage, KeyTime:
			continue
		}
		keys = append(keys, k)
	}
	// Sort fields by key names
	sort.Strings(keys)
	// Print fields using logfmt format
	for _, k := range keys {
		buf.WriteByte(' ')
		colorPrint(buf, k, green)
		buf.WriteByte('=')
		if err := writeValue(buf, fields[k]); err != nil {
			return err
		}
	}
	buf.WriteByte('\n')
	_, err := o.w.Write(buf.Bytes())
	return err
}

type logfmtOutput struct {
	w io.Writer
}

// NewLogfmtOutput returns a new output using logstash JSON schema v1
func NewLogfmtOutput(w io.Writer) Output {
	return logfmtOutput{w: w}
}

func (o logfmtOutput) Write(fields map[string]interface{}) error {
	buf := bufPool.Get().(*bytes.Buffer)
	defer func() {
		buf.Reset()
		bufPool.Put(buf)
	}()
	// Gather field keys
	keys := []string{}
	for k := range fields {
		switch k {
		case KeyLevel, KeyMessage, KeyTime:
			continue
		}
		keys = append(keys, k)
	}
	// Sort fields by key names
	sort.Strings(keys)
	// Prepend default fields in a specific order
	keys = append([]string{KeyLevel, KeyMessage, KeyTime}, keys...)
	l := len(keys)
	for i, k := range keys {
		buf.Write([]byte(k))
		buf.WriteByte('=')
		if err := writeValue(buf, fields[k]); err != nil {
			return err
		}
		if i+1 < l {
			buf.WriteByte(' ')
		} else {
			buf.WriteByte('\n')
		}
	}
	_, err := o.w.Write(buf.Bytes())
	return err
}

// NewJSONOutput returns a new JSON output with the given writer.
func NewJSONOutput(w io.Writer) Output {
	enc := json.NewEncoder(w)
	return OutputFunc(func(fields map[string]interface{}) error {
		return enc.Encode(fields)
	})
}

// NewLogstashOutput returns an output to generate logstash friendly JSON format.
func NewLogstashOutput(w io.Writer) Output {
	return OutputFunc(func(fields map[string]interface{}) error {
		lsf := map[string]interface{}{
			"@version": 1,
		}
		for k, v := range fields {
			switch k {
			case KeyTime:
				k = "@timestamp"
			case KeyLevel:
				if s, ok := v.(string); ok {
					v = strings.ToUpper(s)
				}
			}
			if t, ok := v.(time.Time); ok {
				lsf[k] = t.Format(time.RFC3339)
			} else {
				lsf[k] = v
			}
		}
		b, err := json.Marshal(lsf)
		if err != nil {
			return err
		}
		_, err = w.Write(b)
		return err
	})
}

// NewUIDOutput returns an output filter adding a globally unique id (using github.com/rs/xid)
// to all message going thru this output. The o parameter defines the next output to pass data
// to.
func NewUIDOutput(field string, o Output) Output {
	return OutputFunc(func(fields map[string]interface{}) error {
		fields[field] = xid.New().String()
		return o.Write(fields)
	})
}

// NewTrimOutput trims any field of type string with a value length greater than maxLen
// to maxLen.
func NewTrimOutput(maxLen int, o Output) Output {
	return OutputFunc(func(fields map[string]interface{}) error {
		for k, v := range fields {
			if s, ok := v.(string); ok && len(s) > maxLen {
				fields[k] = s[:maxLen]
			}
		}
		return o.Write(fields)
	})
}

// NewTrimFieldsOutput trims listed field fields of type string with a value length greater than maxLen
// to maxLen.
func NewTrimFieldsOutput(trimFields []string, maxLen int, o Output) Output {
	return OutputFunc(func(fields map[string]interface{}) error {
		for _, f := range trimFields {
			if s, ok := fields[f].(string); ok && len(s) > maxLen {
				fields[f] = s[:maxLen]
			}
		}
		return o.Write(fields)
	})
}


================================================
FILE: output_examples_test.go
================================================
package xlog_test

import (
	"log/syslog"

	"github.com/rs/xlog"
)

func Example_combinedOutputs() {
	conf := xlog.Config{
		Output: xlog.NewOutputChannel(xlog.MultiOutput{
			// Output interesting messages to console
			0: xlog.FilterOutput{
				Cond: func(fields map[string]interface{}) bool {
					val, found := fields["type"]
					return found && val == "interesting"
				},
				Output: xlog.NewConsoleOutput(),
			},
			// Also setup by-level loggers
			1: xlog.LevelOutput{
				// Send debug messages to console if they match type
				Debug: xlog.FilterOutput{
					Cond: func(fields map[string]interface{}) bool {
						val, found := fields["type"]
						return found && val == "interesting"
					},
					Output: xlog.NewConsoleOutput(),
				},
			},
			// Also send everything over syslog
			2: xlog.NewSyslogOutput("", "", ""),
		}),
	}

	lh := xlog.NewHandler(conf)
	_ = lh
}

func ExampleMultiOutput() {
	conf := xlog.Config{
		Output: xlog.NewOutputChannel(xlog.MultiOutput{
			// Output everything to console
			0: xlog.NewConsoleOutput(),
			// and also to local syslog
			1: xlog.NewSyslogOutput("", "", ""),
		}),
	}
	lh := xlog.NewHandler(conf)
	_ = lh
}

func ExampleFilterOutput() {
	conf := xlog.Config{
		Output: xlog.NewOutputChannel(xlog.FilterOutput{
			// Match messages containing a field type = interesting
			Cond: func(fields map[string]interface{}) bool {
				val, found := fields["type"]
				return found && val == "interesting"
			},
			// Output matching messages to the console
			Output: xlog.NewConsoleOutput(),
		}),
	}

	lh := xlog.NewHandler(conf)
	_ = lh
}

func ExampleLevelOutput() {
	conf := xlog.Config{
		Output: xlog.NewOutputChannel(xlog.LevelOutput{
			// Send debug message to console
			Debug: xlog.NewConsoleOutput(),
			// and error messages to syslog
			Error: xlog.NewSyslogOutput("", "", ""),
			// other levels are discarded
		}),
	}

	lh := xlog.NewHandler(conf)
	_ = lh
}

func ExampleNewSyslogWriter() {
	conf := xlog.Config{
		Output: xlog.NewOutputChannel(xlog.LevelOutput{
			Debug: xlog.NewLogstashOutput(xlog.NewSyslogWriter("", "", syslog.LOG_LOCAL0|syslog.LOG_DEBUG, "")),
			Info:  xlog.NewLogstashOutput(xlog.NewSyslogWriter("", "", syslog.LOG_LOCAL0|syslog.LOG_INFO, "")),
			Warn:  xlog.NewLogstashOutput(xlog.NewSyslogWriter("", "", syslog.LOG_LOCAL0|syslog.LOG_WARNING, "")),
			Error: xlog.NewLogstashOutput(xlog.NewSyslogWriter("", "", syslog.LOG_LOCAL0|syslog.LOG_ERR, "")),
		}),
	}

	lh := xlog.NewHandler(conf)
	_ = lh
}


================================================
FILE: output_syslog.go
================================================
// +build !windows

package xlog

import (
	"io"
	"log/syslog"
)

// NewSyslogOutput returns JSONOutputs in a LevelOutput with writers set to syslog
// with the proper priority added to a LOG_USER facility.
// If network and address are empty, Dial will connect to the local syslog server.
func NewSyslogOutput(network, address, tag string) Output {
	return NewSyslogOutputFacility(network, address, tag, syslog.LOG_USER)
}

// NewSyslogOutputFacility returns JSONOutputs in a LevelOutput with writers set to syslog
// with the proper priority added to the passed facility.
// If network and address are empty, Dial will connect to the local syslog server.
func NewSyslogOutputFacility(network, address, tag string, facility syslog.Priority) Output {
	o := LevelOutput{
		Debug: NewJSONOutput(NewSyslogWriter(network, address, facility|syslog.LOG_DEBUG, tag)),
		Info:  NewJSONOutput(NewSyslogWriter(network, address, facility|syslog.LOG_INFO, tag)),
		Warn:  NewJSONOutput(NewSyslogWriter(network, address, facility|syslog.LOG_WARNING, tag)),
		Error: NewJSONOutput(NewSyslogWriter(network, address, facility|syslog.LOG_ERR, tag)),
	}
	return o
}

// NewSyslogWriter returns a writer ready to be used with output modules.
// If network and address are empty, Dial will connect to the local syslog server.
func NewSyslogWriter(network, address string, prio syslog.Priority, tag string) io.Writer {
	s, err := syslog.Dial(network, address, prio, tag)
	if err != nil {
		m := "syslog dial error: " + err.Error()
		critialLogger.Print(m)
		panic(m)
	}
	return s
}


================================================
FILE: output_test.go
================================================
package xlog

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"os"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

type testOutput struct {
	err error
	w   chan map[string]interface{}
}

func newTestOutput() *testOutput {
	return &testOutput{w: make(chan map[string]interface{}, 10)}
}

func newTestOutputErr(err error) *testOutput {
	return &testOutput{w: make(chan map[string]interface{}, 10), err: err}
}

func (o *testOutput) Write(fields map[string]interface{}) (err error) {
	o.w <- fields
	return o.err
}

func (o *testOutput) reset() {
	o.w = make(chan map[string]interface{}, 10)
}

func (o *testOutput) empty() bool {
	select {
	case <-o.w:
		return false
	default:
		return true
	}
}

func (o *testOutput) get() map[string]interface{} {
	select {
	case last := <-o.w:
		return last
	case <-time.After(2 * time.Second):
		return nil
	}
}

func TestOutputChannel(t *testing.T) {
	o := newTestOutput()
	oc := NewOutputChannel(o)
	defer oc.Close()
	oc.input <- F{"foo": "bar"}
	assert.Equal(t, F{"foo": "bar"}, F(o.get()))
}

func TestOutputChannelError(t *testing.T) {
	// Trigger error path
	r, w := io.Pipe()
	go func() {
		critialLoggerMux.Lock()
		defer critialLoggerMux.Unlock()
		oldCritialLogger := critialLogger
		critialLogger = log.New(w, "", 0)
		o := newTestOutputErr(errors.New("some error"))
		oc := NewOutputChannel(o)
		oc.input <- F{"foo": "bar"}
		o.get()
		oc.Close()
		critialLogger = oldCritialLogger
		w.Close()
	}()
	b, err := ioutil.ReadAll(r)
	assert.NoError(t, err)
	assert.Contains(t, string(b), "cannot write log message: some error")
}

func TestOutputChannelClose(t *testing.T) {
	oc := NewOutputChannel(newTestOutput())
	defer oc.Close()
	assert.NotNil(t, oc.stop)
	oc.Close()
	assert.Nil(t, oc.stop)
	oc.Close()
}

func TestDiscard(t *testing.T) {
	assert.NoError(t, Discard.Write(F{}))
}

func TestMultiOutput(t *testing.T) {
	o1 := newTestOutput()
	o2 := newTestOutput()
	mo := MultiOutput{o1, o2}
	err := mo.Write(F{"foo": "bar"})
	assert.NoError(t, err)
	assert.Equal(t, F{"foo": "bar"}, F(<-o1.w))
	assert.Equal(t, F{"foo": "bar"}, F(<-o2.w))
}

func TestMultiOutputWithError(t *testing.T) {
	o1 := newTestOutputErr(errors.New("some error"))
	o2 := newTestOutput()
	mo := MultiOutput{o1, o2}
	err := mo.Write(F{"foo": "bar"})
	assert.EqualError(t, err, "some error")
	// Still send data to all outputs
	assert.Equal(t, F{"foo": "bar"}, F(<-o1.w))
	assert.Equal(t, F{"foo": "bar"}, F(<-o2.w))
}

func TestFilterOutput(t *testing.T) {
	o := newTestOutput()
	f := FilterOutput{
		Cond: func(fields map[string]interface{}) bool {
			return fields["foo"] == "bar"
		},
		Output: o,
	}
	err := f.Write(F{"foo": "bar"})
	assert.NoError(t, err)
	assert.Equal(t, F{"foo": "bar"}, F(o.get()))

	o.reset()
	err = f.Write(F{"foo": "baz"})
	assert.NoError(t, err)
	assert.True(t, o.empty())

	f.Output = nil
	err = f.Write(F{"foo": "baz"})
	assert.NoError(t, err)
}

func TestLevelOutput(t *testing.T) {
	oInfo := newTestOutput()
	oError := newTestOutput()
	oFatal := newTestOutput()
	oWarn := &testOutput{err: errors.New("some error")}
	reset := func() {
		oInfo.reset()
		oError.reset()
		oFatal.reset()
		oWarn.reset()
	}
	l := LevelOutput{
		Info:  oInfo,
		Error: oError,
		Fatal: oFatal,
		Warn:  oWarn,
	}

	err := l.Write(F{"level": "fatal", "foo": "bar"})
	assert.NoError(t, err)
	assert.True(t, oInfo.empty())
	assert.True(t, oError.empty())
	assert.Equal(t, F{"level": "fatal", "foo": "bar"}, F(<-oFatal.w))
	assert.True(t, oWarn.empty())

	reset()
	err = l.Write(F{"level": "error", "foo": "bar"})
	assert.NoError(t, err)
	assert.True(t, oInfo.empty())
	assert.Equal(t, F{"level": "error", "foo": "bar"}, F(<-oError.w))
	assert.True(t, oFatal.empty())
	assert.True(t, oWarn.empty())

	reset()
	err = l.Write(F{"level": "info", "foo": "bar"})
	assert.NoError(t, err)
	assert.Equal(t, F{"level": "info", "foo": "bar"}, F(<-oInfo.w))
	assert.True(t, oFatal.empty())
	assert.True(t, oError.empty())
	assert.True(t, oWarn.empty())

	reset()
	err = l.Write(F{"level": "warn", "foo": "bar"})
	assert.EqualError(t, err, "some error")
	assert.True(t, oInfo.empty())
	assert.True(t, oError.empty())
	assert.True(t, oFatal.empty())
	assert.Equal(t, F{"level": "warn", "foo": "bar"}, F(<-oWarn.w))

	reset()
	err = l.Write(F{"level": "debug", "foo": "bar"})
	assert.NoError(t, err)
	assert.True(t, oInfo.empty())
	assert.True(t, oError.empty())
	assert.True(t, oFatal.empty())
	assert.True(t, oWarn.empty())

	reset()
	err = l.Write(F{"foo": "bar"})
	assert.NoError(t, err)
	assert.True(t, oInfo.empty())
	assert.True(t, oError.empty())
	assert.True(t, oFatal.empty())
	assert.True(t, oWarn.empty())
}

func TestSyslogOutput(t *testing.T) {
	buf := bytes.NewBuffer(nil)
	critialLoggerMux.Lock()
	oldCritialLogger := critialLogger
	critialLogger = log.New(buf, "", 0)
	defer func() {
		critialLogger = oldCritialLogger
		critialLoggerMux.Unlock()
	}()
	m := NewSyslogOutput("udp", "127.0.0.1:1234", "mytag")
	assert.IsType(t, LevelOutput{}, m)
	assert.Panics(t, func() {
		NewSyslogOutput("tcp", "an invalid host name", "mytag")
	})
	assert.Regexp(t, "syslog dial error: dial tcp:.*missing port in address.*", buf.String())
}

func TestRecorderOutput(t *testing.T) {
	o := RecorderOutput{}
	o.Write(F{"foo": "bar"})
	o.Write(F{"bar": "baz"})
	assert.Equal(t, []F{{"foo": "bar"}, {"bar": "baz"}}, o.Messages)
	o.Reset()
	assert.Equal(t, []F{}, o.Messages)
}

func TestNewConsoleOutput(t *testing.T) {
	old := isTerminal
	defer func() { isTerminal = old }()
	isTerminal = func(w io.Writer) bool { return true }
	c := NewConsoleOutput()
	if assert.IsType(t, consoleOutput{}, c) {
		assert.Equal(t, os.Stderr, c.(consoleOutput).w)
	}
	isTerminal = func(w io.Writer) bool { return false }
	c = NewConsoleOutput()
	if assert.IsType(t, logfmtOutput{}, c) {
		assert.Equal(t, os.Stderr, c.(logfmtOutput).w)
	}
}

func TestNewConsoleOutputW(t *testing.T) {
	b := bytes.NewBuffer([]byte{})
	c := NewConsoleOutputW(b, NewLogfmtOutput(b))
	assert.IsType(t, logfmtOutput{}, c)
	old := isTerminal
	defer func() { isTerminal = old }()
	isTerminal = func(w io.Writer) bool { return true }
	c = NewConsoleOutputW(b, NewLogfmtOutput(b))
	if assert.IsType(t, consoleOutput{}, c) {
		assert.Equal(t, b, c.(consoleOutput).w)
	}
}

func TestConsoleOutput(t *testing.T) {
	buf := &bytes.Buffer{}
	c := consoleOutput{w: buf}
	err := c.Write(F{"message": "some message", "level": "info", "time": time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC), "foo": "bar"})
	assert.NoError(t, err)
	assert.Equal(t, "2000/01/02 03:04:05 \x1b[34mINFO\x1b[0m some message \x1b[32mfoo\x1b[0m=bar\n", buf.String())
	buf.Reset()
	err = c.Write(F{"message": "some debug", "level": "debug"})
	assert.NoError(t, err)
	assert.Equal(t, "\x1b[37mDEBU\x1b[0m some debug\n", buf.String())
	buf.Reset()
	err = c.Write(F{"message": "some warning", "level": "warn"})
	assert.NoError(t, err)
	assert.Equal(t, "\x1b[33mWARN\x1b[0m some warning\n", buf.String())
	buf.Reset()
	err = c.Write(F{"message": "some error", "level": "error"})
	assert.NoError(t, err)
	assert.Equal(t, "\x1b[31mERRO\x1b[0m some error\n", buf.String())
}

func TestLogfmtOutput(t *testing.T) {
	buf := &bytes.Buffer{}
	c := NewLogfmtOutput(buf)
	err := c.Write(F{
		"time":    time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC),
		"message": "some message",
		"level":   "info",
		"string":  "foo",
		"null":    nil,
		"quoted":  "needs \" quotes",
		"err":     errors.New("error"),
		"errq":    errors.New("error with \" quote"),
	})
	assert.NoError(t, err)
	assert.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())
}

func TestJSONOutput(t *testing.T) {
	buf := &bytes.Buffer{}
	j := NewJSONOutput(buf)
	err := j.Write(F{"message": "some message", "level": "info", "foo": "bar"})
	assert.NoError(t, err)
	assert.Equal(t, "{\"foo\":\"bar\",\"level\":\"info\",\"message\":\"some message\"}\n", buf.String())
}

func TestLogstashOutput(t *testing.T) {
	buf := &bytes.Buffer{}
	o := NewLogstashOutput(buf)
	err := o.Write(F{
		"message": "some message",
		"level":   "info",
		"time":    time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC),
		"file":    "test.go:234",
		"foo":     "bar",
	})
	assert.NoError(t, err)
	assert.Equal(t, "{\"@timestamp\":\"2000-01-02T03:04:05Z\",\"@version\":1,\"file\":\"test.go:234\",\"foo\":\"bar\",\"level\":\"INFO\",\"message\":\"some message\"}", buf.String())
}

func TestUIDOutput(t *testing.T) {
	o := newTestOutput()
	i := NewUIDOutput("id", o)
	err := i.Write(F{"message": "some message", "level": "info", "foo": "bar"})
	last := o.get()
	assert.NoError(t, err)
	assert.NotNil(t, last["id"])
	assert.Len(t, last["id"], 20)
}

func TestTrimOutput(t *testing.T) {
	o := newTestOutput()
	i := NewTrimOutput(10, o)
	err := i.Write(F{"short": "short", "long": "too long message", "number": 20})
	last := o.get()
	assert.NoError(t, err)
	assert.Equal(t, "short", last["short"])
	assert.Equal(t, "too long m", last["long"])
	assert.Equal(t, 20, last["number"])
}

func TestTrimFieldsOutput(t *testing.T) {
	o := newTestOutput()
	i := NewTrimFieldsOutput([]string{"short", "trim", "number"}, 10, o)
	err := i.Write(F{"short": "short", "long": "too long message", "trim": "too long message", "number": 20})
	last := o.get()
	assert.NoError(t, err)
	assert.Equal(t, "short", last["short"])
	assert.Equal(t, "too long m", last["trim"])
	assert.Equal(t, "too long message", last["long"])
	assert.Equal(t, 20, last["number"])
}


================================================
FILE: std.go
================================================
package xlog

import "fmt"

var std = New(Config{
	Output: NewConsoleOutput(),
})

// SetLogger changes the global logger instance
func SetLogger(logger Logger) {
	std = logger
}

// Debug calls the Debug() method on the default logger
func Debug(v ...interface{}) {
	f := extractFields(&v)
	std.OutputF(LevelDebug, 2, fmt.Sprint(v...), f)
}

// Debugf calls the Debugf() method on the default logger
func Debugf(format string, v ...interface{}) {
	f := extractFields(&v)
	std.OutputF(LevelDebug, 2, fmt.Sprintf(format, v...), f)
}

// Info calls the Info() method on the default logger
func Info(v ...interface{}) {
	f := extractFields(&v)
	std.OutputF(LevelInfo, 2, fmt.Sprint(v...), f)
}

// Infof calls the Infof() method on the default logger
func Infof(format string, v ...interface{}) {
	f := extractFields(&v)
	std.OutputF(LevelInfo, 2, fmt.Sprintf(format, v...), f)
}

// Warn calls the Warn() method on the default logger
func Warn(v ...interface{}) {
	f := extractFields(&v)
	std.OutputF(LevelWarn, 2, fmt.Sprint(v...), f)
}

// Warnf calls the Warnf() method on the default logger
func Warnf(format string, v ...interface{}) {
	f := extractFields(&v)
	std.OutputF(LevelWarn, 2, fmt.Sprintf(format, v...), f)
}

// Error calls the Error() method on the default logger
func Error(v ...interface{}) {
	f := extractFields(&v)
	std.OutputF(LevelError, 2, fmt.Sprint(v...), f)
}

// Errorf calls the Errorf() method on the default logger
//
// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last
// argument to workaround go vet false alarm.
func Errorf(format string, v ...interface{}) {
	f := extractFields(&v)
	if f != nil {
		// Let user add a %v at the end of the message when fields are passed to satisfy go vet
		l := len(format)
		if l > 2 && format[l-2] == '%' && format[l-1] == 'v' {
			format = format[0 : l-2]
		}
	}
	std.OutputF(LevelError, 2, fmt.Sprintf(format, v...), f)
}

// Fatal calls the Fatal() method on the default logger
func Fatal(v ...interface{}) {
	f := extractFields(&v)
	std.OutputF(LevelFatal, 2, fmt.Sprint(v...), f)
	if l, ok := std.(*logger); ok {
		if o, ok := l.output.(*OutputChannel); ok {
			o.Close()
		}
	}
	exit1()
}

// Fatalf calls the Fatalf() method on the default logger
//
// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last
// argument to workaround go vet false alarm.
func Fatalf(format string, v ...interface{}) {
	f := extractFields(&v)
	if f != nil {
		// Let user add a %v at the end of the message when fields are passed to satisfy go vet
		l := len(format)
		if l > 2 && format[l-2] == '%' && format[l-1] == 'v' {
			format = format[0 : l-2]
		}
	}
	std.OutputF(LevelFatal, 2, fmt.Sprintf(format, v...), f)
	if l, ok := std.(*logger); ok {
		if o, ok := l.output.(*OutputChannel); ok {
			o.Close()
		}
	}
	exit1()
}


================================================
FILE: std_example_test.go
================================================
package xlog_test

import "github.com/rs/xlog"

func ExampleSetLogger() {
	xlog.SetLogger(xlog.New(xlog.Config{
		Level:  xlog.LevelInfo,
		Output: xlog.NewConsoleOutput(),
		Fields: xlog.F{
			"role": "my-service",
		},
	}))
}


================================================
FILE: std_test.go
================================================
package xlog

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestGlobalLogger(t *testing.T) {
	o := newTestOutput()
	oldStd := std
	defer func() { std = oldStd }()
	SetLogger(New(Config{Output: o}))
	Debug("test")
	last := o.get()
	assert.Equal(t, "test", last["message"])
	assert.Equal(t, "debug", last["level"])
	o.reset()
	Debugf("test")
	last = o.get()
	assert.Equal(t, "test", last["message"])
	assert.Equal(t, "debug", last["level"])
	o.reset()
	Info("test")
	last = o.get()
	assert.Equal(t, "test", last["message"])
	assert.Equal(t, "info", last["level"])
	o.reset()
	Infof("test")
	last = o.get()
	assert.Equal(t, "test", last["message"])
	assert.Equal(t, "info", last["level"])
	o.reset()
	Warn("test")
	last = o.get()
	assert.Equal(t, "test", last["message"])
	assert.Equal(t, "warn", last["level"])
	o.reset()
	Warnf("test")
	last = o.get()
	assert.Equal(t, "test", last["message"])
	assert.Equal(t, "warn", last["level"])
	o.reset()
	Error("test")
	last = o.get()
	assert.Equal(t, "test", last["message"])
	assert.Equal(t, "error", last["level"])
	o.reset()
	Errorf("test")
	last = o.get()
	assert.Equal(t, "test", last["message"])
	assert.Equal(t, "error", last["level"])
	o.reset()
	oldExit := exit1
	exit1 = func() {}
	defer func() { exit1 = oldExit }()
	Fatal("test")
	last = o.get()
	assert.Equal(t, "test", last["message"])
	assert.Equal(t, "fatal", last["level"])
	o.reset()
	Fatalf("test")
	last = o.get()
	assert.Equal(t, "test", last["message"])
	assert.Equal(t, "fatal", last["level"])
	o.reset()
}

func TestStdError(t *testing.T) {
	o := newTestOutput()
	oldStd := std
	defer func() { std = oldStd }()
	SetLogger(New(Config{Output: o}))
	Error("test", F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "std_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "error", "message": "test", "foo": "bar"}, last)
}

func TestStdErrorf(t *testing.T) {
	o := newTestOutput()
	oldStd := std
	defer func() { std = oldStd }()
	SetLogger(New(Config{Output: o}))
	Errorf("test %d%v", 1, F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "std_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "error", "message": "test 1", "foo": "bar"}, last)
}

func TestStdFatal(t *testing.T) {
	e := exit1
	exited := 0
	exit1 = func() { exited++ }
	defer func() { exit1 = e }()
	o := newTestOutput()
	oldStd := std
	defer func() { std = oldStd }()
	SetLogger(New(Config{Output: o}))
	Fatal("test", F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "std_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "fatal", "message": "test", "foo": "bar"}, last)
	assert.Equal(t, 1, exited)
}

func TestStdFatalf(t *testing.T) {
	e := exit1
	exited := 0
	exit1 = func() { exited++ }
	defer func() { exit1 = e }()
	o := newTestOutput()
	oldStd := std
	defer func() { std = oldStd }()
	SetLogger(New(Config{Output: o}))
	Fatalf("test %d%v", 1, F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "std_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "fatal", "message": "test 1", "foo": "bar"}, last)
	assert.Equal(t, 1, exited)
}


================================================
FILE: util.go
================================================
package xlog

import (
	"encoding/json"
	"fmt"
	"io"
	"strings"
)

type color int

const (
	red    color = 31
	green  color = 32
	yellow color = 33
	blue   color = 34
	gray   color = 37
)

func colorPrint(w io.Writer, s string, c color) {
	w.Write([]byte{0x1b, '[', byte('0' + c/10), byte('0' + c%10), 'm'})
	w.Write([]byte(s))
	w.Write([]byte("\x1b[0m"))
}

func needsQuotedValueRune(r rune) bool {
	return r <= ' ' || r == '=' || r == '"'
}

// writeValue writes a value on the writer in a logfmt compatible way
func writeValue(w io.Writer, v interface{}) (err error) {
	switch v := v.(type) {
	case nil:
		_, err = w.Write([]byte("null"))
	case string:
		if strings.IndexFunc(v, needsQuotedValueRune) != -1 {
			var b []byte
			b, err = json.Marshal(v)
			if err == nil {
				w.Write(b)
			}
		} else {
			_, err = w.Write([]byte(v))
		}
	case error:
		s := v.Error()
		err = writeValue(w, s)
	default:
		s := fmt.Sprint(v)
		err = writeValue(w, s)
	}
	return
}


================================================
FILE: util_test.go
================================================
package xlog

import (
	"bytes"
	"errors"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestColorPrint(t *testing.T) {
	buf := &bytes.Buffer{}
	colorPrint(buf, "test", red)
	assert.Equal(t, "\x1b[31mtest\x1b[0m", buf.String())
	buf.Reset()
	colorPrint(buf, "test", green)
	assert.Equal(t, "\x1b[32mtest\x1b[0m", buf.String())
	buf.Reset()
	colorPrint(buf, "test", yellow)
	assert.Equal(t, "\x1b[33mtest\x1b[0m", buf.String())
	buf.Reset()
	colorPrint(buf, "test", blue)
	assert.Equal(t, "\x1b[34mtest\x1b[0m", buf.String())
	buf.Reset()
	colorPrint(buf, "test", gray)
	assert.Equal(t, "\x1b[37mtest\x1b[0m", buf.String())
}

func TestNeedsQuotedValueRune(t *testing.T) {
	assert.True(t, needsQuotedValueRune('='))
	assert.True(t, needsQuotedValueRune('"'))
	assert.True(t, needsQuotedValueRune(' '))
	assert.False(t, needsQuotedValueRune('a'))
	assert.False(t, needsQuotedValueRune('\''))
}

func TestWriteValue(t *testing.T) {
	buf := &bytes.Buffer{}
	write := func(v interface{}) string {
		buf.Reset()
		err := writeValue(buf, v)
		if err == nil {
			return buf.String()
		}
		return ""
	}
	assert.Equal(t, `foobar`, write(`foobar`))
	assert.Equal(t, `"foo=bar"`, write(`foo=bar`))
	assert.Equal(t, `"foo bar"`, write(`foo bar`))
	assert.Equal(t, `"foo\"bar"`, write(`foo"bar`))
	assert.Equal(t, `"foo\nbar"`, write("foo\nbar"))
	assert.Equal(t, `null`, write(nil))
	assert.Equal(t, `"2000-01-02 03:04:05 +0000 UTC"`, write(time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC)))
	assert.Equal(t, `"error \"with quote\""`, write(errors.New(`error "with quote"`)))
}


================================================
FILE: xlog.go
================================================
// Package xlog is a logger coupled with HTTP net/context aware middleware.
//
// Unlike 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.
//
// Features:
//
//     - Per request log context
//     - Per request and/or per message key/value fields
//     - Log levels (Debug, Info, Warn, Error)
//     - Color output when terminal is detected
//     - Custom output (JSON, logfmt, …)
//     - Automatic gathering of request context like User-Agent, IP etc.
//     - Drops message rather than blocking execution
//     - Easy access logging thru github.com/rs/xaccess
//
// It works best in combination with github.com/rs/xhandler.
package xlog // import "github.com/rs/xlog"

import (
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

// Logger defines the interface for a xlog compatible logger
type Logger interface {
	// Implements io.Writer so it can be set a output of log.Logger
	io.Writer

	// SetField sets a field on the logger's context. All future messages on this logger
	// will have this field set.
	SetField(name string, value interface{})
	// GetFields returns all the fields set on the logger
	GetFields() F
	// Debug logs a debug message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Debug(v ...interface{})
	// Debug logs a debug message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Debugf(format string, v ...interface{})
	// Info logs a info message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Info(v ...interface{})
	// Info logs a info message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Infof(format string, v ...interface{})
	// Warn logs a warning message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Warn(v ...interface{})
	// Warn logs a warning message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Warnf(format string, v ...interface{})
	// Error logs an error message. If last parameter is a map[string]string, it's content
	// is added as fields to the message.
	Error(v ...interface{})
	// Error logs an error message with format. If last parameter is a map[string]string,
	// it's content is added as fields to the message.
	Errorf(format string, v ...interface{})
	// Fatal logs an error message followed by a call to os.Exit(1). If last parameter is a
	// map[string]string, it's content is added as fields to the message.
	Fatal(v ...interface{})
	// Fatalf logs an error message with format followed by a call to ox.Exit(1). If last
	// parameter is a map[string]string, it's content is added as fields to the message.
	Fatalf(format string, v ...interface{})
	// Output mimics std logger interface
	Output(calldepth int, s string) error
	// OutputF outputs message with fields.
	OutputF(level Level, calldepth int, msg string, fields map[string]interface{})
}

// LoggerCopier defines a logger with copy support
type LoggerCopier interface {
	// Copy returns a copy of the logger
	Copy() Logger
}

// Config defines logger's configuration
type Config struct {
	// Level is the maximum level to output, logs with lower level are discarded.
	Level Level
	// Fields defines default fields to use with all messages.
	Fields map[string]interface{}
	// Output to use to write log messages to.
	//
	// You should always wrap your output with an OutputChannel otherwise your
	// logger will be connected to its output synchronously.
	Output Output
	// DisablePooling removes the use of a sync.Pool for cases where logger
	// instances are needed beyond the scope of a request handler. This option
	// puts a greater pressure on GC and increases the amount of memory allocated
	// and freed. Use only if persistent loggers are a requirement.
	DisablePooling bool
}

// F represents a set of log message fields
type F map[string]interface{}

type logger struct {
	level          Level
	output         Output
	fields         F
	disablePooling bool
}

// Common field names for log messages.
var (
	KeyTime    = "time"
	KeyMessage = "message"
	KeyLevel   = "level"
	KeyFile    = "file"
)

var now = time.Now
var exit1 = func() { os.Exit(1) }

// critialLogger is a logger to use when xlog is not able to deliver a message
var critialLogger = log.New(os.Stderr, "xlog: ", log.Ldate|log.Ltime|log.LUTC|log.Lshortfile)

var loggerPool = &sync.Pool{
	New: func() interface{} {
		return &logger{}
	},
}

// New manually creates a logger.
//
// This function should only be used out of a request. Use FromContext in request.
func New(c Config) Logger {
	var l *logger
	if c.DisablePooling {
		l = &logger{}
	} else {
		l = loggerPool.Get().(*logger)
	}
	l.level = c.Level
	l.output = c.Output
	if l.output == nil {
		l.output = NewOutputChannel(NewConsoleOutput())
	}
	for k, v := range c.Fields {
		l.SetField(k, v)
	}
	l.disablePooling = c.DisablePooling
	return l
}

// Copy returns a copy of the passed logger if the logger implements
// LoggerCopier or the NopLogger otherwise.
func Copy(l Logger) Logger {
	if l, ok := l.(LoggerCopier); ok {
		return l.Copy()
	}
	return NopLogger
}

// Copy returns a copy of the logger
func (l *logger) Copy() Logger {
	l2 := &logger{
		level:          l.level,
		output:         l.output,
		fields:         map[string]interface{}{},
		disablePooling: l.disablePooling,
	}
	for k, v := range l.fields {
		l2.fields[k] = v
	}
	return l2
}

// close returns the logger to the pool for reuse
func (l *logger) close() {
	if !l.disablePooling {
		l.level = 0
		l.output = nil
		l.fields = nil
		loggerPool.Put(l)
	}
}

func (l *logger) send(level Level, calldepth int, msg string, fields map[string]interface{}) {
	if level < l.level || l.output == nil {
		return
	}
	data := make(map[string]interface{}, 4+len(fields)+len(l.fields))
	data[KeyTime] = now()
	data[KeyLevel] = level.String()
	data[KeyMessage] = msg
	if _, file, line, ok := runtime.Caller(calldepth); ok {
		data[KeyFile] = path.Base(file) + ":" + strconv.FormatInt(int64(line), 10)
	}
	for k, v := range fields {
		data[k] = v
	}
	if l.fields != nil {
		for k, v := range l.fields {
			data[k] = v
		}
	}
	if err := l.output.Write(data); err != nil {
		critialLogger.Print("send error: ", err.Error())
	}
}

func extractFields(v *[]interface{}) map[string]interface{} {
	if l := len(*v); l > 0 {
		if f, ok := (*v)[l-1].(map[string]interface{}); ok {
			*v = (*v)[:l-1]
			return f
		}
		if f, ok := (*v)[l-1].(F); ok {
			*v = (*v)[:l-1]
			return f
		}
	}
	return nil
}

// SetField implements Logger interface
func (l *logger) SetField(name string, value interface{}) {
	if l.fields == nil {
		l.fields = map[string]interface{}{}
	}
	l.fields[name] = value
}

// GetFields implements Logger interface
func (l *logger) GetFields() F {
	return l.fields
}

// Output implements Logger interface
func (l *logger) OutputF(level Level, calldepth int, msg string, fields map[string]interface{}) {
	l.send(level, calldepth+1, msg, fields)
}

// Debug implements Logger interface
func (l *logger) Debug(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelDebug, 2, fmt.Sprint(v...), f)
}

// Debugf implements Logger interface
func (l *logger) Debugf(format string, v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelDebug, 2, fmt.Sprintf(format, v...), f)
}

// Info implements Logger interface
func (l *logger) Info(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelInfo, 2, fmt.Sprint(v...), f)
}

// Infof implements Logger interface
func (l *logger) Infof(format string, v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelInfo, 2, fmt.Sprintf(format, v...), f)
}

// Warn implements Logger interface
func (l *logger) Warn(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelWarn, 2, fmt.Sprint(v...), f)
}

// Warnf implements Logger interface
func (l *logger) Warnf(format string, v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelWarn, 2, fmt.Sprintf(format, v...), f)
}

// Error implements Logger interface
func (l *logger) Error(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelError, 2, fmt.Sprint(v...), f)
}

// Errorf implements Logger interface
//
// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last
// argument to workaround go vet false alarm.
func (l *logger) Errorf(format string, v ...interface{}) {
	f := extractFields(&v)
	if f != nil {
		// Let user add a %v at the end of the message when fields are passed to satisfy go vet
		l := len(format)
		if l > 2 && format[l-2] == '%' && format[l-1] == 'v' {
			format = format[0 : l-2]
		}
	}
	l.send(LevelError, 2, fmt.Sprintf(format, v...), f)
}

// Fatal implements Logger interface
func (l *logger) Fatal(v ...interface{}) {
	f := extractFields(&v)
	l.send(LevelFatal, 2, fmt.Sprint(v...), f)
	if o, ok := l.output.(*OutputChannel); ok {
		o.Close()
	}
	exit1()
}

// Fatalf implements Logger interface
//
// Go vet users: you may append %v at the end of you format when using xlog.F{} as a last
// argument to workaround go vet false alarm.
func (l *logger) Fatalf(format string, v ...interface{}) {
	f := extractFields(&v)
	if f != nil {
		// Let user add a %v at the end of the message when fields are passed to satisfy go vet
		l := len(format)
		if l > 2 && format[l-2] == '%' && format[l-1] == 'v' {
			format = format[0 : l-2]
		}
	}
	l.send(LevelFatal, 2, fmt.Sprintf(format, v...), f)
	if o, ok := l.output.(*OutputChannel); ok {
		o.Close()
	}
	exit1()
}

// Write implements io.Writer interface
func (l *logger) Write(p []byte) (int, error) {
	msg := strings.TrimRight(string(p), "\n")
	l.send(LevelInfo, 4, msg, nil)
	if o, ok := l.output.(*OutputChannel); ok {
		o.Flush()
	}
	return len(p), nil
}

// Output implements common logger interface
func (l *logger) Output(calldepth int, s string) error {
	l.send(LevelInfo, 2, s, nil)
	return nil
}


================================================
FILE: xlog_bench_test.go
================================================
package xlog

import "testing"

func BenchmarkSend(b *testing.B) {
	l := New(Config{Output: Discard, Fields: F{"a": "b"}}).(*logger)
	b.ResetTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		l.send(0, 0, "test", F{"foo": "bar", "bar": "baz"})
	}
}


================================================
FILE: xlog_examples_test.go
================================================
// +build go1.7

package xlog_test

import (
	"context"
	"errors"
	"log"

	"github.com/rs/xlog"
)

func Example_log() {
	ctx := context.TODO()
	l := xlog.FromContext(ctx)

	// Log a simple message
	l.Debug("message")

	if err := errors.New("some error"); err != nil {
		l.Errorf("Some error happened: %v", err)
	}

	// With optional fields
	l.Debugf("foo %s", "bar", xlog.F{
		"field": "value",
	})
}

func Example_stdlog() {
	// Define logger conf
	conf := xlog.Config{
		Output: xlog.NewConsoleOutput(),
	}

	// Remove timestamp and other decorations of the std logger
	log.SetFlags(0)

	// Plug a xlog instance to Go's std logger
	log.SetOutput(xlog.New(conf))
}


================================================
FILE: xlog_test.go
================================================
package xlog

import (
	"io"
	"io/ioutil"
	"log"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

var fakeNow = time.Date(0, 0, 0, 0, 0, 0, 0, time.Local)
var critialLoggerMux = sync.Mutex{}

func init() {
	now = func() time.Time {
		return fakeNow
	}
}

func TestNew(t *testing.T) {
	oc := NewOutputChannel(newTestOutput())
	defer oc.Close()
	c := Config{
		Level:  LevelError,
		Output: oc,
		Fields: F{"foo": "bar"},
	}
	L := New(c)
	l, ok := L.(*logger)
	if assert.True(t, ok) {
		assert.Equal(t, LevelError, l.level)
		assert.Equal(t, c.Output, l.output)
		assert.Equal(t, F{"foo": "bar"}, F(l.fields))
		// Ensure l.fields is a clone
		c.Fields["bar"] = "baz"
		assert.Equal(t, F{"foo": "bar"}, F(l.fields))
		assert.Equal(t, false, l.disablePooling)
		l.close()
	}
}

func TestNewPoolDisabled(t *testing.T) {
	oc := NewOutputChannel(newTestOutput())
	defer oc.Close()
	originalPool := loggerPool
	defer func(p *sync.Pool) {
		loggerPool = originalPool
	}(originalPool)
	loggerPool = &sync.Pool{
		New: func() interface{} {
			assert.Fail(t, "pool used when disabled")
			return nil
		},
	}
	c := Config{
		Level:          LevelError,
		Output:         oc,
		Fields:         F{"foo": "bar"},
		DisablePooling: true,
	}
	L := New(c)
	l, ok := L.(*logger)
	if assert.True(t, ok) {
		assert.Equal(t, LevelError, l.level)
		assert.Equal(t, c.Output, l.output)
		assert.Equal(t, F{"foo": "bar"}, F(l.fields))
		// Ensure l.fields is a clone
		c.Fields["bar"] = "baz"
		assert.Equal(t, F{"foo": "bar"}, F(l.fields))
		assert.Equal(t, true, l.disablePooling)
		l.close()
		// Assert again to ensure close does not remove internal state
		assert.Equal(t, LevelError, l.level)
		assert.Equal(t, c.Output, l.output)
		assert.Equal(t, F{"foo": "bar"}, F(l.fields))
		// Ensure l.fields is a clone
		c.Fields["bar"] = "baz"
		assert.Equal(t, F{"foo": "bar"}, F(l.fields))
		assert.Equal(t, true, l.disablePooling)
	}
}

func TestCopy(t *testing.T) {
	oc := NewOutputChannel(newTestOutput())
	defer oc.Close()
	c := Config{
		Level:  LevelError,
		Output: oc,
		Fields: F{"foo": "bar"},
	}
	l := New(c).(*logger)
	l2 := Copy(l).(*logger)
	assert.Equal(t, l.output, l2.output)
	assert.Equal(t, l.level, l2.level)
	assert.Equal(t, l.fields, l2.fields)
	l2.SetField("bar", "baz")
	assert.Equal(t, F{"foo": "bar"}, l.fields)
	assert.Equal(t, F{"foo": "bar", "bar": "baz"}, l2.fields)

	assert.Equal(t, NopLogger, Copy(NopLogger))
	assert.Equal(t, NopLogger, Copy(nil))
}

func TestNewDefautOutput(t *testing.T) {
	L := New(Config{})
	l, ok := L.(*logger)
	if assert.True(t, ok) {
		assert.NotNil(t, l.output)
		l.close()
	}
}

func TestSend(t *testing.T) {
	o := newTestOutput()
	l := New(Config{Output: o}).(*logger)
	l.send(LevelDebug, 1, "test", F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "debug", "message": "test", "foo": "bar"}, last)

	l.SetField("bar", "baz")
	l.send(LevelInfo, 1, "test", F{"foo": "bar"})
	last = <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "info", "message": "test", "foo": "bar", "bar": "baz"}, last)

	l = New(Config{Output: o, Level: 1}).(*logger)
	o.reset()
	l.send(0, 2, "test", F{"foo": "bar"})
	assert.True(t, o.empty())
}

func TestSendDrop(t *testing.T) {
	t.Skip()
	r, w := io.Pipe()
	go func() {
		critialLoggerMux.Lock()
		defer critialLoggerMux.Unlock()
		oldCritialLogger := critialLogger
		critialLogger = log.New(w, "", 0)
		o := newTestOutput()
		oc := NewOutputChannelBuffer(Discard, 1)
		l := New(Config{Output: oc}).(*logger)
		l.send(LevelDebug, 2, "test", F{"foo": "bar"})
		l.send(LevelDebug, 2, "test", F{"foo": "bar"})
		l.send(LevelDebug, 2, "test", F{"foo": "bar"})
		o.get()
		o.get()
		o.get()
		oc.Close()
		critialLogger = oldCritialLogger
		w.Close()
	}()
	b, err := ioutil.ReadAll(r)
	assert.NoError(t, err)
	assert.Contains(t, string(b), "send error: buffer full")
}

func TestExtractFields(t *testing.T) {
	v := []interface{}{"a", 1, map[string]interface{}{"foo": "bar"}}
	f := extractFields(&v)
	assert.Equal(t, map[string]interface{}{"foo": "bar"}, f)
	assert.Equal(t, []interface{}{"a", 1}, v)

	v = []interface{}{map[string]interface{}{"foo": "bar"}, "a", 1}
	f = extractFields(&v)
	assert.Nil(t, f)
	assert.Equal(t, []interface{}{map[string]interface{}{"foo": "bar"}, "a", 1}, v)

	v = []interface{}{"a", 1, F{"foo": "bar"}}
	f = extractFields(&v)
	assert.Equal(t, map[string]interface{}{"foo": "bar"}, f)
	assert.Equal(t, []interface{}{"a", 1}, v)

	v = []interface{}{}
	f = extractFields(&v)
	assert.Nil(t, f)
	assert.Equal(t, []interface{}{}, v)
}

func TestGetFields(t *testing.T) {
	oc := NewOutputChannelBuffer(Discard, 1)
	l := New(Config{Output: oc}).(*logger)
	l.SetField("k", "v")
	assert.Equal(t, F{"k": "v"}, l.GetFields())
}

func TestDebug(t *testing.T) {
	o := newTestOutput()
	l := New(Config{Output: o}).(*logger)
	l.Debug("test", F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "debug", "message": "test", "foo": "bar"}, last)
}

func TestDebugf(t *testing.T) {
	o := newTestOutput()
	l := New(Config{Output: o}).(*logger)
	l.Debugf("test %d", 1, F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "debug", "message": "test 1", "foo": "bar"}, last)
}

func TestInfo(t *testing.T) {
	o := newTestOutput()
	l := New(Config{Output: o}).(*logger)
	l.Info("test", F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "info", "message": "test", "foo": "bar"}, last)
}

func TestInfof(t *testing.T) {
	o := newTestOutput()
	l := New(Config{Output: o}).(*logger)
	l.Infof("test %d", 1, F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "info", "message": "test 1", "foo": "bar"}, last)
}

func TestWarn(t *testing.T) {
	o := newTestOutput()
	l := New(Config{Output: o}).(*logger)
	l.Warn("test", F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "warn", "message": "test", "foo": "bar"}, last)
}

func TestWarnf(t *testing.T) {
	o := newTestOutput()
	l := New(Config{Output: o}).(*logger)
	l.Warnf("test %d", 1, F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "warn", "message": "test 1", "foo": "bar"}, last)
}

func TestError(t *testing.T) {
	o := newTestOutput()
	l := New(Config{Output: o}).(*logger)
	l.Error("test", F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "error", "message": "test", "foo": "bar"}, last)
}

func TestErrorf(t *testing.T) {
	o := newTestOutput()
	l := New(Config{Output: o}).(*logger)
	l.Errorf("test %d%v", 1, F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "error", "message": "test 1", "foo": "bar"}, last)
}

func TestFatal(t *testing.T) {
	e := exit1
	exited := 0
	exit1 = func() { exited++ }
	defer func() { exit1 = e }()
	o := newTestOutput()
	l := New(Config{Output: NewOutputChannel(o)}).(*logger)
	l.Fatal("test", F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "fatal", "message": "test", "foo": "bar"}, last)
	assert.Equal(t, 1, exited)
}

func TestFatalf(t *testing.T) {
	e := exit1
	exited := 0
	exit1 = func() { exited++ }
	defer func() { exit1 = e }()
	o := newTestOutput()
	l := New(Config{Output: NewOutputChannel(o)}).(*logger)
	l.Fatalf("test %d%v", 1, F{"foo": "bar"})
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "fatal", "message": "test 1", "foo": "bar"}, last)
	assert.Equal(t, 1, exited)
}

func TestWrite(t *testing.T) {
	o := newTestOutput()
	xl := New(Config{Output: NewOutputChannel(o)}).(*logger)
	l := log.New(xl, "prefix ", 0)
	l.Printf("test")
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "info", "message": "prefix test"}, last)
}

func TestOutput(t *testing.T) {
	o := newTestOutput()
	l := New(Config{Output: o}).(*logger)
	l.Output(2, "test")
	last := <-o.w
	assert.Contains(t, last["file"], "log_test.go:")
	delete(last, "file")
	assert.Equal(t, map[string]interface{}{"time": fakeNow, "level": "info", "message": "test"}, last)
}
Download .txt
gitextract_0x8cvxva/

├── .travis.yml
├── LICENSE
├── README.md
├── handler.go
├── handler_examples_test.go
├── handler_pre17.go
├── handler_pre17_test.go
├── handler_test.go
├── internal/
│   └── term/
│       ├── LICENSE
│       ├── term.go
│       ├── term_appengine.go
│       ├── term_darwin.go
│       ├── term_freebsd.go
│       ├── term_linux.go
│       ├── term_notwindows.go
│       ├── term_openbsd.go
│       └── term_windows.go
├── levels.go
├── levels_test.go
├── nop.go
├── nop_test.go
├── output.go
├── output_examples_test.go
├── output_syslog.go
├── output_test.go
├── std.go
├── std_example_test.go
├── std_test.go
├── util.go
├── util_test.go
├── xlog.go
├── xlog_bench_test.go
├── xlog_examples_test.go
└── xlog_test.go
Download .txt
SYMBOL INDEX (233 symbols across 30 files)

FILE: handler.go
  type key (line 13) | type key
  constant logKey (line 16) | logKey key = iota
  constant idKey (line 17) | idKey
  function IDFromContext (line 21) | func IDFromContext(ctx context.Context) (xid.ID, bool) {
  function IDFromRequest (line 27) | func IDFromRequest(r *http.Request) (xid.ID, bool) {
  function FromContext (line 36) | func FromContext(ctx context.Context) Logger {
  function FromRequest (line 49) | func FromRequest(r *http.Request) Logger {
  function NewContext (line 57) | func NewContext(ctx context.Context, l Logger) context.Context {
  function NewHandler (line 64) | func NewHandler(c Config) func(http.Handler) http.Handler {
  function URLHandler (line 85) | func URLHandler(name string) func(next http.Handler) http.Handler {
  function MethodHandler (line 97) | func MethodHandler(name string) func(next http.Handler) http.Handler {
  function RequestHandler (line 109) | func RequestHandler(name string) func(next http.Handler) http.Handler {
  function RemoteAddrHandler (line 121) | func RemoteAddrHandler(name string) func(next http.Handler) http.Handler {
  function UserAgentHandler (line 135) | func UserAgentHandler(name string) func(next http.Handler) http.Handler {
  function RefererHandler (line 149) | func RefererHandler(name string) func(next http.Handler) http.Handler {
  function RequestIDHandler (line 170) | func RequestIDHandler(name, headerName string) func(next http.Handler) h...

FILE: handler_examples_test.go
  function Example_handler (line 15) | func Example_handler() {

FILE: handler_pre17.go
  type key (line 14) | type key
  constant logKey (line 17) | logKey key = iota
  constant idKey (line 18) | idKey
  function IDFromContext (line 22) | func IDFromContext(ctx context.Context) (xid.ID, bool) {
  function FromContext (line 29) | func FromContext(ctx context.Context) Logger {
  function NewContext (line 41) | func NewContext(ctx context.Context, l Logger) context.Context {
  function NewHandler (line 48) | func NewHandler(c Config) func(xhandler.HandlerC) xhandler.HandlerC {
  function URLHandler (line 66) | func URLHandler(name string) func(next xhandler.HandlerC) xhandler.Handl...
  function MethodHandler (line 77) | func MethodHandler(name string) func(next xhandler.HandlerC) xhandler.Ha...
  function RequestHandler (line 88) | func RequestHandler(name string) func(next xhandler.HandlerC) xhandler.H...
  function RemoteAddrHandler (line 99) | func RemoteAddrHandler(name string) func(next xhandler.HandlerC) xhandle...
  function UserAgentHandler (line 112) | func UserAgentHandler(name string) func(next xhandler.HandlerC) xhandler...
  function RefererHandler (line 125) | func RefererHandler(name string) func(next xhandler.HandlerC) xhandler.H...
  function RequestIDHandler (line 145) | func RequestIDHandler(name, headerName string) func(next xhandler.Handle...

FILE: handler_pre17_test.go
  function TestFromContext (line 16) | func TestFromContext(t *testing.T) {
  function TestNewHandler (line 24) | func TestNewHandler(t *testing.T) {
  function TestURLHandler (line 44) | func TestURLHandler(t *testing.T) {
  function TestMethodHandler (line 56) | func TestMethodHandler(t *testing.T) {
  function TestRequestHandler (line 68) | func TestRequestHandler(t *testing.T) {
  function TestRemoteAddrHandler (line 81) | func TestRemoteAddrHandler(t *testing.T) {
  function TestRemoteAddrHandlerIPv6 (line 93) | func TestRemoteAddrHandlerIPv6(t *testing.T) {
  function TestUserAgentHandler (line 105) | func TestUserAgentHandler(t *testing.T) {
  function TestRefererHandler (line 119) | func TestRefererHandler(t *testing.T) {
  function TestRequestIDHandler (line 133) | func TestRequestIDHandler(t *testing.T) {

FILE: handler_test.go
  function TestFromContext (line 15) | func TestFromContext(t *testing.T) {
  function TestNewHandler (line 23) | func TestNewHandler(t *testing.T) {
  function TestURLHandler (line 43) | func TestURLHandler(t *testing.T) {
  function TestMethodHandler (line 55) | func TestMethodHandler(t *testing.T) {
  function TestRequestHandler (line 67) | func TestRequestHandler(t *testing.T) {
  function TestRemoteAddrHandler (line 80) | func TestRemoteAddrHandler(t *testing.T) {
  function TestRemoteAddrHandlerIPv6 (line 92) | func TestRemoteAddrHandlerIPv6(t *testing.T) {
  function TestUserAgentHandler (line 104) | func TestUserAgentHandler(t *testing.T) {
  function TestRefererHandler (line 118) | func TestRefererHandler(t *testing.T) {
  function TestRequestIDHandler (line 132) | func TestRequestIDHandler(t *testing.T) {

FILE: internal/term/term.go
  type fder (line 10) | type fder interface

FILE: internal/term/term_appengine.go
  function IsTerminal (line 13) | func IsTerminal(w io.Writer) bool {

FILE: internal/term/term_darwin.go
  constant ioctlReadTermios (line 10) | ioctlReadTermios = syscall.TIOCGETA

FILE: internal/term/term_freebsd.go
  constant ioctlReadTermios (line 7) | ioctlReadTermios = syscall.TIOCGETA

FILE: internal/term/term_linux.go
  constant ioctlReadTermios (line 12) | ioctlReadTermios = syscall.TCGETS

FILE: internal/term/term_notwindows.go
  function IsTerminal (line 17) | func IsTerminal(w io.Writer) bool {

FILE: internal/term/term_openbsd.go
  constant ioctlReadTermios (line 5) | ioctlReadTermios = syscall.TIOCGETA

FILE: internal/term/term_windows.go
  function IsTerminal (line 23) | func IsTerminal(w io.Writer) bool {

FILE: levels.go
  type Level (line 10) | type Level
    method UnmarshalText (line 44) | func (l *Level) UnmarshalText(text []byte) (err error) {
    method String (line 62) | func (l Level) String() string {
    method MarshalText (line 82) | func (l Level) MarshalText() ([]byte, error) {
  constant LevelDebug (line 14) | LevelDebug Level = iota
  constant LevelInfo (line 15) | LevelInfo
  constant LevelWarn (line 16) | LevelWarn
  constant LevelError (line 17) | LevelError
  constant LevelFatal (line 18) | LevelFatal
  function LevelFromString (line 37) | func LevelFromString(t string) (Level, error) {

FILE: levels_test.go
  function TestLevelFromString (line 9) | func TestLevelFromString(t *testing.T) {
  function TestLevelUnmarshalerText (line 29) | func TestLevelUnmarshalerText(t *testing.T) {
  function TestLevelString (line 49) | func TestLevelString(t *testing.T) {
  function TestLevelMarshalerText (line 58) | func TestLevelMarshalerText(t *testing.T) {

FILE: nop.go
  type nop (line 3) | type nop struct
    method SetField (line 8) | func (n nop) SetField(name string, value interface{}) {}
    method GetFields (line 10) | func (n nop) GetFields() F { return map[string]interface{}{} }
    method OutputF (line 12) | func (n nop) OutputF(level Level, calldepth int, msg string, fields ma...
    method Debug (line 14) | func (n nop) Debug(v ...interface{}) {}
    method Debugf (line 16) | func (n nop) Debugf(format string, v ...interface{}) {}
    method Info (line 18) | func (n nop) Info(v ...interface{}) {}
    method Infof (line 20) | func (n nop) Infof(format string, v ...interface{}) {}
    method Warn (line 22) | func (n nop) Warn(v ...interface{}) {}
    method Warnf (line 24) | func (n nop) Warnf(format string, v ...interface{}) {}
    method Error (line 26) | func (n nop) Error(v ...interface{}) {}
    method Errorf (line 28) | func (n nop) Errorf(format string, v ...interface{}) {}
    method Fatal (line 30) | func (n nop) Fatal(v ...interface{}) {
    method Fatalf (line 34) | func (n nop) Fatalf(format string, v ...interface{}) {
    method Write (line 38) | func (n nop) Write(p []byte) (int, error) { return len(p), nil }
    method Output (line 40) | func (n nop) Output(calldepth int, s string) error {

FILE: nop_test.go
  function TestNopLogger (line 5) | func TestNopLogger(t *testing.T) {

FILE: output.go
  type Output (line 19) | type Output interface
  type OutputFunc (line 26) | type OutputFunc
    method Write (line 28) | func (of OutputFunc) Write(fields map[string]interface{}) error {
  type OutputChannel (line 33) | type OutputChannel struct
    method Write (line 76) | func (oc *OutputChannel) Write(fields map[string]interface{}) (err err...
    method Flush (line 88) | func (oc *OutputChannel) Flush() {
    method Close (line 102) | func (oc *OutputChannel) Close() {
  function NewOutputChannel (line 45) | func NewOutputChannel(o Output) *OutputChannel {
  function NewOutputChannelBuffer (line 51) | func NewOutputChannelBuffer(o Output, bufSize int) *OutputChannel {
  type MultiOutput (line 125) | type MultiOutput
    method Write (line 127) | func (m MultiOutput) Write(fields map[string]interface{}) (err error) {
  type FilterOutput (line 139) | type FilterOutput struct
    method Write (line 144) | func (f FilterOutput) Write(fields map[string]interface{}) (err error) {
  type LevelOutput (line 155) | type LevelOutput struct
    method Write (line 163) | func (l LevelOutput) Write(fields map[string]interface{}) error {
  type RecorderOutput (line 184) | type RecorderOutput struct
    method Write (line 188) | func (l *RecorderOutput) Write(fields map[string]interface{}) error {
    method Reset (line 198) | func (l *RecorderOutput) Reset() {
  type consoleOutput (line 202) | type consoleOutput struct
    method Write (line 223) | func (o consoleOutput) Write(fields map[string]interface{}) error {
  function NewConsoleOutput (line 210) | func NewConsoleOutput() Output {
  function NewConsoleOutputW (line 216) | func NewConsoleOutputW(w io.Writer, noTerm Output) Output {
  type logfmtOutput (line 274) | type logfmtOutput struct
    method Write (line 283) | func (o logfmtOutput) Write(fields map[string]interface{}) error {
  function NewLogfmtOutput (line 279) | func NewLogfmtOutput(w io.Writer) Output {
  function NewJSONOutput (line 320) | func NewJSONOutput(w io.Writer) Output {
  function NewLogstashOutput (line 328) | func NewLogstashOutput(w io.Writer) Output {
  function NewUIDOutput (line 360) | func NewUIDOutput(field string, o Output) Output {
  function NewTrimOutput (line 369) | func NewTrimOutput(maxLen int, o Output) Output {
  function NewTrimFieldsOutput (line 382) | func NewTrimFieldsOutput(trimFields []string, maxLen int, o Output) Outp...

FILE: output_examples_test.go
  function Example_combinedOutputs (line 9) | func Example_combinedOutputs() {
  function ExampleMultiOutput (line 40) | func ExampleMultiOutput() {
  function ExampleFilterOutput (line 53) | func ExampleFilterOutput() {
  function ExampleLevelOutput (line 70) | func ExampleLevelOutput() {
  function ExampleNewSyslogWriter (line 85) | func ExampleNewSyslogWriter() {

FILE: output_syslog.go
  function NewSyslogOutput (line 13) | func NewSyslogOutput(network, address, tag string) Output {
  function NewSyslogOutputFacility (line 20) | func NewSyslogOutputFacility(network, address, tag string, facility sysl...
  function NewSyslogWriter (line 32) | func NewSyslogWriter(network, address string, prio syslog.Priority, tag ...

FILE: output_test.go
  type testOutput (line 16) | type testOutput struct
    method Write (line 29) | func (o *testOutput) Write(fields map[string]interface{}) (err error) {
    method reset (line 34) | func (o *testOutput) reset() {
    method empty (line 38) | func (o *testOutput) empty() bool {
    method get (line 47) | func (o *testOutput) get() map[string]interface{} {
  function newTestOutput (line 21) | func newTestOutput() *testOutput {
  function newTestOutputErr (line 25) | func newTestOutputErr(err error) *testOutput {
  function TestOutputChannel (line 56) | func TestOutputChannel(t *testing.T) {
  function TestOutputChannelError (line 64) | func TestOutputChannelError(t *testing.T) {
  function TestOutputChannelClose (line 85) | func TestOutputChannelClose(t *testing.T) {
  function TestDiscard (line 94) | func TestDiscard(t *testing.T) {
  function TestMultiOutput (line 98) | func TestMultiOutput(t *testing.T) {
  function TestMultiOutputWithError (line 108) | func TestMultiOutputWithError(t *testing.T) {
  function TestFilterOutput (line 119) | func TestFilterOutput(t *testing.T) {
  function TestLevelOutput (line 141) | func TestLevelOutput(t *testing.T) {
  function TestSyslogOutput (line 207) | func TestSyslogOutput(t *testing.T) {
  function TestRecorderOutput (line 224) | func TestRecorderOutput(t *testing.T) {
  function TestNewConsoleOutput (line 233) | func TestNewConsoleOutput(t *testing.T) {
  function TestNewConsoleOutputW (line 248) | func TestNewConsoleOutputW(t *testing.T) {
  function TestConsoleOutput (line 261) | func TestConsoleOutput(t *testing.T) {
  function TestLogfmtOutput (line 281) | func TestLogfmtOutput(t *testing.T) {
  function TestJSONOutput (line 298) | func TestJSONOutput(t *testing.T) {
  function TestLogstashOutput (line 306) | func TestLogstashOutput(t *testing.T) {
  function TestUIDOutput (line 320) | func TestUIDOutput(t *testing.T) {
  function TestTrimOutput (line 330) | func TestTrimOutput(t *testing.T) {
  function TestTrimFieldsOutput (line 341) | func TestTrimFieldsOutput(t *testing.T) {

FILE: std.go
  function SetLogger (line 10) | func SetLogger(logger Logger) {
  function Debug (line 15) | func Debug(v ...interface{}) {
  function Debugf (line 21) | func Debugf(format string, v ...interface{}) {
  function Info (line 27) | func Info(v ...interface{}) {
  function Infof (line 33) | func Infof(format string, v ...interface{}) {
  function Warn (line 39) | func Warn(v ...interface{}) {
  function Warnf (line 45) | func Warnf(format string, v ...interface{}) {
  function Error (line 51) | func Error(v ...interface{}) {
  function Errorf (line 60) | func Errorf(format string, v ...interface{}) {
  function Fatal (line 73) | func Fatal(v ...interface{}) {
  function Fatalf (line 88) | func Fatalf(format string, v ...interface{}) {

FILE: std_example_test.go
  function ExampleSetLogger (line 5) | func ExampleSetLogger() {

FILE: std_test.go
  function TestGlobalLogger (line 9) | func TestGlobalLogger(t *testing.T) {
  function TestStdError (line 69) | func TestStdError(t *testing.T) {
  function TestStdErrorf (line 81) | func TestStdErrorf(t *testing.T) {
  function TestStdFatal (line 93) | func TestStdFatal(t *testing.T) {
  function TestStdFatalf (line 110) | func TestStdFatalf(t *testing.T) {

FILE: util.go
  type color (line 10) | type color
  constant red (line 13) | red    color = 31
  constant green (line 14) | green  color = 32
  constant yellow (line 15) | yellow color = 33
  constant blue (line 16) | blue   color = 34
  constant gray (line 17) | gray   color = 37
  function colorPrint (line 20) | func colorPrint(w io.Writer, s string, c color) {
  function needsQuotedValueRune (line 26) | func needsQuotedValueRune(r rune) bool {
  function writeValue (line 31) | func writeValue(w io.Writer, v interface{}) (err error) {

FILE: util_test.go
  function TestColorPrint (line 12) | func TestColorPrint(t *testing.T) {
  function TestNeedsQuotedValueRune (line 30) | func TestNeedsQuotedValueRune(t *testing.T) {
  function TestWriteValue (line 38) | func TestWriteValue(t *testing.T) {

FILE: xlog.go
  type Logger (line 37) | type Logger interface
  type LoggerCopier (line 83) | type LoggerCopier interface
  type Config (line 89) | type Config struct
  type F (line 107) | type F
  type logger (line 109) | type logger struct
    method Copy (line 168) | func (l *logger) Copy() Logger {
    method close (line 182) | func (l *logger) close() {
    method send (line 191) | func (l *logger) send(level Level, calldepth int, msg string, fields m...
    method SetField (line 230) | func (l *logger) SetField(name string, value interface{}) {
    method GetFields (line 238) | func (l *logger) GetFields() F {
    method OutputF (line 243) | func (l *logger) OutputF(level Level, calldepth int, msg string, field...
    method Debug (line 248) | func (l *logger) Debug(v ...interface{}) {
    method Debugf (line 254) | func (l *logger) Debugf(format string, v ...interface{}) {
    method Info (line 260) | func (l *logger) Info(v ...interface{}) {
    method Infof (line 266) | func (l *logger) Infof(format string, v ...interface{}) {
    method Warn (line 272) | func (l *logger) Warn(v ...interface{}) {
    method Warnf (line 278) | func (l *logger) Warnf(format string, v ...interface{}) {
    method Error (line 284) | func (l *logger) Error(v ...interface{}) {
    method Errorf (line 293) | func (l *logger) Errorf(format string, v ...interface{}) {
    method Fatal (line 306) | func (l *logger) Fatal(v ...interface{}) {
    method Fatalf (line 319) | func (l *logger) Fatalf(format string, v ...interface{}) {
    method Write (line 336) | func (l *logger) Write(p []byte) (int, error) {
    method Output (line 346) | func (l *logger) Output(calldepth int, s string) error {
  function New (line 139) | func New(c Config) Logger {
  function Copy (line 160) | func Copy(l Logger) Logger {
  function extractFields (line 215) | func extractFields(v *[]interface{}) map[string]interface{} {

FILE: xlog_bench_test.go
  function BenchmarkSend (line 5) | func BenchmarkSend(b *testing.B) {

FILE: xlog_examples_test.go
  function Example_log (line 13) | func Example_log() {
  function Example_stdlog (line 30) | func Example_stdlog() {

FILE: xlog_test.go
  function init (line 17) | func init() {
  function TestNew (line 23) | func TestNew(t *testing.T) {
  function TestNewPoolDisabled (line 45) | func TestNewPoolDisabled(t *testing.T) {
  function TestCopy (line 86) | func TestCopy(t *testing.T) {
  function TestNewDefautOutput (line 107) | func TestNewDefautOutput(t *testing.T) {
  function TestSend (line 116) | func TestSend(t *testing.T) {
  function TestSendDrop (line 138) | func TestSendDrop(t *testing.T) {
  function TestExtractFields (line 164) | func TestExtractFields(t *testing.T) {
  function TestGetFields (line 186) | func TestGetFields(t *testing.T) {
  function TestDebug (line 193) | func TestDebug(t *testing.T) {
  function TestDebugf (line 203) | func TestDebugf(t *testing.T) {
  function TestInfo (line 213) | func TestInfo(t *testing.T) {
  function TestInfof (line 223) | func TestInfof(t *testing.T) {
  function TestWarn (line 233) | func TestWarn(t *testing.T) {
  function TestWarnf (line 243) | func TestWarnf(t *testing.T) {
  function TestError (line 253) | func TestError(t *testing.T) {
  function TestErrorf (line 263) | func TestErrorf(t *testing.T) {
  function TestFatal (line 273) | func TestFatal(t *testing.T) {
  function TestFatalf (line 288) | func TestFatalf(t *testing.T) {
  function TestWrite (line 303) | func TestWrite(t *testing.T) {
  function TestOutput (line 314) | func TestOutput(t *testing.T) {
Condensed preview — 34 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (103K chars).
[
  {
    "path": ".travis.yml",
    "chars": 123,
    "preview": "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 ./."
  },
  {
    "path": "LICENSE",
    "chars": 1080,
    "preview": "Copyright (c) 2015 Olivier Poitrey <rs@dailymotion.com>\n\nPermission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "README.md",
    "chars": 9138,
    "preview": ":warning: **Check [zerolog](https://github.com/rs/zerolog), the successor of xlog.**\n\n\n# HTTP Handler Logger\n\n[![godoc]("
  },
  {
    "path": "handler.go",
    "chars": 5795,
    "preview": "// +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\tlo"
  },
  {
    "path": "handler_examples_test.go",
    "chars": 1642,
    "preview": "// +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.c"
  },
  {
    "path": "handler_pre17.go",
    "chars": 5599,
    "preview": "// +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"
  },
  {
    "path": "handler_pre17_test.go",
    "chars": 4473,
    "preview": "// +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/xhandle"
  },
  {
    "path": "handler_test.go",
    "chars": 3980,
    "preview": "// +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/"
  },
  {
    "path": "internal/term/LICENSE",
    "chars": 1082,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Simon Eskildsen\n\nPermission is hereby granted, free of charge, to any person o"
  },
  {
    "path": "internal/term/term.go",
    "chars": 289,
    "preview": "// Based on ssh/terminal:\n// Copyright 2011 The Go Authors. All rights reserved.\n// Use of this source code is governed "
  },
  {
    "path": "internal/term/term_appengine.go",
    "chars": 335,
    "preview": "// Based on ssh/terminal:\n// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed "
  },
  {
    "path": "internal/term/term_darwin.go",
    "chars": 260,
    "preview": "// Based on ssh/terminal:\n// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed "
  },
  {
    "path": "internal/term/term_freebsd.go",
    "chars": 79,
    "preview": "package term\n\nimport (\n\t\"syscall\"\n)\n\nconst ioctlReadTermios = syscall.TIOCGETA\n"
  },
  {
    "path": "internal/term/term_linux.go",
    "chars": 280,
    "preview": "// Based on ssh/terminal:\n// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed "
  },
  {
    "path": "internal/term/term_notwindows.go",
    "chars": 597,
    "preview": "// Based on ssh/terminal:\n// Copyright 2011 The Go Authors. All rights reserved.\n// Use of this source code is governed "
  },
  {
    "path": "internal/term/term_openbsd.go",
    "chars": 79,
    "preview": "package term\r\n\r\nimport \"syscall\"\r\n\r\nconst ioctlReadTermios = syscall.TIOCGETA\r\n"
  },
  {
    "path": "internal/term/term_windows.go",
    "chars": 680,
    "preview": "// Based on ssh/terminal:\n// Copyright 2011 The Go Authors. All rights reserved.\n// Use of this source code is governed "
  },
  {
    "path": "levels.go",
    "chars": 2046,
    "preview": "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\t"
  },
  {
    "path": "levels_test.go",
    "chars": 2235,
    "preview": "package xlog\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLevelFromString(t *testing.T) {\n\tl,"
  },
  {
    "path": "nop.go",
    "chars": 969,
    "preview": "package xlog\n\ntype nop struct{}\n\n// NopLogger is an no-op implementation of xlog.Logger\nvar NopLogger = &nop{}\n\nfunc (n "
  },
  {
    "path": "nop_test.go",
    "chars": 478,
    "preview": "package xlog\n\nimport \"testing\"\n\nfunc TestNopLogger(t *testing.T) {\n\t// cheap cover score upper\n\tNopLogger.SetField(\"name"
  },
  {
    "path": "output.go",
    "chars": 9163,
    "preview": "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/"
  },
  {
    "path": "output_examples_test.go",
    "chars": 2499,
    "preview": "package xlog_test\n\nimport (\n\t\"log/syslog\"\n\n\t\"github.com/rs/xlog\"\n)\n\nfunc Example_combinedOutputs() {\n\tconf := xlog.Confi"
  },
  {
    "path": "output_syslog.go",
    "chars": 1561,
    "preview": "// +build !windows\n\npackage xlog\n\nimport (\n\t\"io\"\n\t\"log/syslog\"\n)\n\n// NewSyslogOutput returns JSONOutputs in a LevelOutpu"
  },
  {
    "path": "output_test.go",
    "chars": 9546,
    "preview": "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/test"
  },
  {
    "path": "std.go",
    "chars": 2854,
    "preview": "package xlog\n\nimport \"fmt\"\n\nvar std = New(Config{\n\tOutput: NewConsoleOutput(),\n})\n\n// SetLogger changes the global logge"
  },
  {
    "path": "std_example_test.go",
    "chars": 228,
    "preview": "package xlog_test\n\nimport \"github.com/rs/xlog\"\n\nfunc ExampleSetLogger() {\n\txlog.SetLogger(xlog.New(xlog.Config{\n\t\tLevel:"
  },
  {
    "path": "std_test.go",
    "chars": 3294,
    "preview": "package xlog\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestGlobalLogger(t *testing.T) {\n\to := "
  },
  {
    "path": "util.go",
    "chars": 965,
    "preview": "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  co"
  },
  {
    "path": "util_test.go",
    "chars": 1579,
    "preview": "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 TestColorPri"
  },
  {
    "path": "xlog.go",
    "chars": 10278,
    "preview": "// Package xlog is a logger coupled with HTTP net/context aware middleware.\n//\n// Unlike most loggers, xlog will never b"
  },
  {
    "path": "xlog_bench_test.go",
    "chars": 254,
    "preview": "package xlog\n\nimport \"testing\"\n\nfunc BenchmarkSend(b *testing.B) {\n\tl := New(Config{Output: Discard, Fields: F{\"a\": \"b\"}"
  },
  {
    "path": "xlog_examples_test.go",
    "chars": 666,
    "preview": "// +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"
  },
  {
    "path": "xlog_test.go",
    "chars": 9215,
    "preview": "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\nva"
  }
]

About this extraction

This page contains the full source code of the rs/xlog GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 34 files (91.2 KB), approximately 26.5k tokens, and a symbol index with 233 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!