diff --git a/.drone.yml b/.drone.yml index 7d3c2467e..80ade219e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -80,7 +80,7 @@ pipeline: image: plugins/docker repo: drone/drone secrets: [ docker_username, docker_password ] - tag: [ 0.8, 0.8.0, 0.8.0-rc.3 ] + tag: [ 0.8, 0.8.0 ] when: event: tag @@ -89,7 +89,7 @@ pipeline: repo: drone/agent dockerfile: Dockerfile.agent secrets: [ docker_username, docker_password ] - tag: [ 0.8, 0.8.0, 0.8.0-rc.3 ] + tag: [ 0.8, 0.8.0 ] when: event: tag diff --git a/cmd/drone-agent/agent.go b/cmd/drone-agent/agent.go index 5ec3d62e0..96fd38472 100644 --- a/cmd/drone-agent/agent.go +++ b/cmd/drone-agent/agent.go @@ -5,7 +5,6 @@ import ( "encoding/json" "io" "io/ioutil" - "log" "os" "strconv" "sync" @@ -21,6 +20,8 @@ import ( "github.com/cncd/pipeline/pipeline/multipart" "github.com/cncd/pipeline/pipeline/rpc" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" "github.com/tevino/abool" "github.com/urfave/cli" oldcontext "golang.org/x/net/context" @@ -38,6 +39,12 @@ func loop(c *cli.Context) error { hostname, _ = os.Hostname() } + if c.BoolT("debug") { + zerolog.SetGlobalLevel(zerolog.DebugLevel) + } else { + zerolog.SetGlobalLevel(zerolog.WarnLevel) + } + // TODO pass version information to grpc server // TODO authenticate to grpc server @@ -81,7 +88,7 @@ func loop(c *cli.Context) error { return } if err := run(ctx, client, filter); err != nil { - log.Printf("build runner encountered error: exiting: %s", err) + log.Error().Err(err).Msg("pipeline done with error") return } } @@ -98,7 +105,8 @@ const ( ) func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error { - log.Println("pipeline: request next execution") + log.Debug(). + Msg("request next execution") meta, _ := metadata.FromOutgoingContext(ctx) ctxmeta := metadata.NewOutgoingContext(context.Background(), meta) @@ -111,11 +119,23 @@ func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error { if work == nil { return nil } - log.Printf("pipeline: received next execution: %s", work.ID) + + logger := log.With(). + Str("repo", extractRepositoryName(work.Config)). + Str("build", extractBuildNumber(work.Config)). + Str("id", work.ID). + Logger() + + logger.Debug(). + Msg("received execution") // new docker engine engine, err := docker.NewEnv() if err != nil { + logger.Error(). + Err(err). + Msg("cannot create docker client") + return err } @@ -129,12 +149,19 @@ func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error { cancelled := abool.New() go func() { + logger.Debug(). + Msg("listen for cancel signal") + if werr := client.Wait(ctx, work.ID); werr != nil { cancelled.SetTo(true) - log.Printf("pipeline: cancel signal received: %s: %s", work.ID, werr) + logger.Warn(). + Err(werr). + Msg("cancel signal received") + cancel() } else { - log.Printf("pipeline: cancel channel closed: %s", work.ID) + logger.Debug(). + Msg("stop listening for cancel signal") } }() @@ -142,10 +169,14 @@ func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error { for { select { case <-ctx.Done(): - log.Printf("pipeline: cancel ping loop: %s", work.ID) + logger.Debug(). + Msg("pipeline done") + return case <-time.After(time.Minute): - log.Printf("pipeline: ping queue: %s", work.ID) + logger.Debug(). + Msg("pipeline lease renewed") + client.Extend(ctx, work.ID) } } @@ -153,13 +184,22 @@ func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error { state := rpc.State{} state.Started = time.Now().Unix() + err = client.Init(ctxmeta, work.ID, state) if err != nil { - log.Printf("pipeline: error signaling pipeline init: %s: %s", work.ID, err) + logger.Error(). + Err(err). + Msg("pipeline initialization failed") } var uploads sync.WaitGroup defaultLogger := pipeline.LogFunc(func(proc *backend.Step, rc multipart.Reader) error { + + loglogger := logger.With(). + Str("image", proc.Image). + Str("stage", proc.Alias). + Logger() + part, rerr := rc.NextPart() if rerr != nil { return rerr @@ -173,10 +213,14 @@ func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error { } } + loglogger.Debug().Msg("log stream opened") + limitedPart := io.LimitReader(part, maxLogsUpload) logstream := rpc.NewLineWriter(client, work.ID, proc.Alias, secrets...) io.Copy(logstream, limitedPart) + loglogger.Debug().Msg("log stream copied") + file := &rpc.File{} file.Mime = "application/json+logs" file.Proc = proc.Alias @@ -185,14 +229,22 @@ func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error { file.Size = len(file.Data) file.Time = time.Now().Unix() + loglogger.Debug(). + Msg("log stream uploading") + if serr := client.Upload(ctxmeta, work.ID, file); serr != nil { - log.Printf("pipeline: cannot upload logs: %s: %s: %s", work.ID, file.Mime, serr) - } else { - log.Printf("pipeline: finish uploading logs: %s: step %s: %s", file.Mime, work.ID, proc.Alias) + loglogger.Error(). + Err(serr). + Msg("log stream upload error") } + loglogger.Debug(). + Msg("log stream upload complete") + defer func() { - log.Printf("pipeline: finish uploading logs: %s: step %s", work.ID, proc.Alias) + loglogger.Debug(). + Msg("log stream closed") + uploads.Done() }() @@ -215,15 +267,34 @@ func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error { file.Meta[key] = value[0] } + loglogger.Debug(). + Str("file", file.Name). + Str("mime", file.Mime). + Msg("file stream uploading") + if serr := client.Upload(ctxmeta, work.ID, file); serr != nil { - log.Printf("pipeline: cannot upload artifact: %s: %s: %s", work.ID, file.Mime, serr) - } else { - log.Printf("pipeline: finish uploading artifact: %s: step %s: %s", file.Mime, work.ID, proc.Alias) + loglogger.Error(). + Err(serr). + Str("file", file.Name). + Str("mime", file.Mime). + Msg("file stream upload error") } + + loglogger.Debug(). + Str("file", file.Name). + Str("mime", file.Mime). + Msg("file stream upload complete") return nil }) defaultTracer := pipeline.TraceFunc(func(state *pipeline.State) error { + proclogger := logger.With(). + Str("image", state.Pipeline.Step.Image). + Str("stage", state.Pipeline.Step.Alias). + Int("exit_code", state.Process.ExitCode). + Bool("exited", state.Process.Exited). + Logger() + procState := rpc.State{ Proc: state.Pipeline.Step.Alias, Exited: state.Process.Exited, @@ -232,9 +303,17 @@ func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error { Finished: time.Now().Unix(), } defer func() { + proclogger.Debug(). + Msg("update step status") + if uerr := client.Update(ctxmeta, work.ID, procState); uerr != nil { - log.Printf("Pipeine: error updating pipeline step status: %s: %s: %s", work.ID, procState.Proc, uerr) + proclogger.Debug(). + Err(uerr). + Msg("update step status error") } + + proclogger.Debug(). + Msg("update step status complete") }() if state.Process.Exited { return nil @@ -287,17 +366,31 @@ func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error { } } - log.Printf("pipeline: execution complete: %s", work.ID) + logger.Debug(). + Str("error", state.Error). + Int("exit_code", state.ExitCode). + Msg("pipeline complete") + + logger.Debug(). + Msg("uploading logs") uploads.Wait() - log.Printf("pipeline: logging complete: %s", work.ID) + logger.Debug(). + Msg("uploading logs complete") + + logger.Debug(). + Str("error", state.Error). + Int("exit_code", state.ExitCode). + Msg("updating pipeline status") err = client.Done(ctxmeta, work.ID, state) if err != nil { - log.Printf("Pipeine: error signaling pipeline done: %s: %s", work.ID, err) + logger.Error().Err(err). + Msg("updating pipeline status failed") } else { - log.Printf("pipeline: done: %s", work.ID) + logger.Debug(). + Msg("updating pipeline status complete") } return nil @@ -318,3 +411,14 @@ func (c *credentials) GetRequestMetadata(oldcontext.Context, ...string) (map[str func (c *credentials) RequireTransportSecurity() bool { return false } + +// extract repository name from the configuration +func extractRepositoryName(config *backend.Config) string { + return config.Stages[0].Steps[0].Environment["DRONE_REPO_NAME"] + "/" + + config.Stages[0].Steps[0].Environment["DRONE_REPO_NAME"] +} + +// extract build number from the configuration +func extractBuildNumber(config *backend.Config) string { + return config.Stages[0].Steps[0].Environment["DRONE_BUILD_NUMBER"] +} diff --git a/cmd/drone-agent/main.go b/cmd/drone-agent/main.go index b31794636..87a2b836a 100644 --- a/cmd/drone-agent/main.go +++ b/cmd/drone-agent/main.go @@ -34,7 +34,7 @@ func main() { Name: "password", Usage: "drone auth password", }, - cli.BoolFlag{ + cli.BoolTFlag{ EnvVar: "DRONE_DEBUG", Name: "debug", Usage: "start the agent in debug mode", diff --git a/vendor/github.com/rs/zerolog/LICENSE b/vendor/github.com/rs/zerolog/LICENSE new file mode 100644 index 000000000..677e07f7a --- /dev/null +++ b/vendor/github.com/rs/zerolog/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Olivier Poitrey + +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. diff --git a/vendor/github.com/rs/zerolog/README.md b/vendor/github.com/rs/zerolog/README.md new file mode 100644 index 000000000..b62133684 --- /dev/null +++ b/vendor/github.com/rs/zerolog/README.md @@ -0,0 +1,315 @@ +# Zero Allocation JSON Logger + +[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/zerolog) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/zerolog/master/LICENSE) [![Build Status](https://travis-ci.org/rs/zerolog.svg?branch=master)](https://travis-ci.org/rs/zerolog) [![Coverage](http://gocover.io/_badge/github.com/rs/zerolog)](http://gocover.io/github.com/rs/zerolog) + +The zerolog package provides a fast and simple logger dedicated to JSON output. + +Zerolog's API is designed to provide both a great developer experience and stunning [performance](#performance). Its unique chaining API allows zerolog to write JSON log events by avoiding allocations and reflection. + +The uber's [zap](https://godoc.org/go.uber.org/zap) library pioneered this approach. Zerolog is taking this concept to the next level with simpler to use API and even better performance. + +To keep the code base and the API simple, zerolog focuses on JSON logging only. As [suggested on reddit](https://www.reddit.com/r/golang/comments/6c9k7n/zerolog_is_now_faster_than_zap/), you may use tools like [humanlog](https://github.com/aybabtme/humanlog) to pretty print JSON on the console during development. + + +## Features + +* Level logging +* Sampling +* Contextual fields +* `context.Context` integration +* `net/http` helpers + +## Usage + +```go +import "github.com/rs/zerolog/log" +``` + +### A global logger can be use for simple logging + +```go +log.Info().Msg("hello world") + +// Output: {"level":"info","time":1494567715,"message":"hello world"} +``` + +NOTE: To import the global logger, import the `log` subpackage `github.com/rs/zerolog/log`. + +```go +log.Fatal(). + Err(err). + Str("service", service). + Msgf("Cannot start %s", service) + +// Output: {"level":"fatal","time":1494567715,"message":"Cannot start myservice","error":"some error","service":"myservice"} +// Exit 1 +``` + +NOTE: Using `Msgf` generates one allocation even when the logger is disabled. + +### Fields can be added to log messages + +```go +log.Info(). + Str("foo", "bar"). + Int("n", 123). + Msg("hello world") + +// Output: {"level":"info","time":1494567715,"foo":"bar","n":123,"message":"hello world"} +``` + +### Create logger instance to manage different outputs + +```go +logger := zerolog.New(os.Stderr).With().Timestamp().Logger() + +logger.Info().Str("foo", "bar").Msg("hello world") + +// Output: {"level":"info","time":1494567715,"message":"hello world","foo":"bar"} +``` + +### Sub-loggers let you chain loggers with additional context + +```go +sublogger := log.With(). + Str("component": "foo"). + Logger() +sublogger.Info().Msg("hello world") + +// Output: {"level":"info","time":1494567715,"message":"hello world","component":"foo"} +``` + +### Level logging + +```go +zerolog.SetGlobalLevel(zerolog.InfoLevel) + +log.Debug().Msg("filtered out message") +log.Info().Msg("routed message") + +if e := log.Debug(); e.Enabled() { + // Compute log output only if enabled. + value := compute() + e.Str("foo": value).Msg("some debug message") +} + +// Output: {"level":"info","time":1494567715,"message":"routed message"} +``` + +### Sub dictionary + +```go +log.Info(). + Str("foo", "bar"). + Dict("dict", zerolog.Dict(). + Str("bar", "baz"). + Int("n", 1) + ).Msg("hello world") + +// Output: {"level":"info","time":1494567715,"foo":"bar","dict":{"bar":"baz","n":1},"message":"hello world"} +``` + +### Customize automatic field names + +```go +zerolog.TimestampFieldName = "t" +zerolog.LevelFieldName = "l" +zerolog.MessageFieldName = "m" + +log.Info().Msg("hello world") + +// Output: {"l":"info","t":1494567715,"m":"hello world"} +``` + +### Log with no level nor message + +```go +log.Log().Str("foo","bar").Msg("") + +// Output: {"time":1494567715,"foo":"bar"} +``` + +### Add contextual fields to the global logger + +```go +log.Logger = log.With().Str("foo", "bar").Logger() +``` + +### Log Sampling + +```go +sampled := log.Sample(10) +sampled.Info().Msg("will be logged every 10 messages") + +// Output: {"time":1494567715,"sample":10,"message":"will be logged every 10 messages"} +``` + +### Pass a sub-logger by context + +```go +ctx := log.With("component", "module").Logger().WithContext(ctx) + +log.Ctx(ctx).Info().Msg("hello world") + +// Output: {"component":"module","level":"info","message":"hello world"} +``` + +### Set as standard logger output + +```go +log := zerolog.New(os.Stdout).With(). + Str("foo", "bar"). + Logger() + +stdlog.SetFlags(0) +stdlog.SetOutput(log) + +stdlog.Print("hello world") + +// Output: {"foo":"bar","message":"hello world"} +``` + +### Integration with `net/http` + +The `github.com/rs/zerolog/hlog` package provides some helpers to integrate zerolog with `http.Handler`. + +In this example we use [alice](https://github.com/justinas/alice) to install logger for better readability. + +```go +log := zerolog.New(os.Stdout).With(). + Timestamp(). + Str("role", "my-service"). + Str("host", host). + Logger() + +c := alice.New() + +// Install the logger handler with default output on the console +c = c.Append(hlog.NewHandler(log)) + +// 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(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) { + hlog.FromRequest(r).Info(). + Str("method", r.Method). + Str("url", r.URL.String()). + Int("status", status). + Int("size", size). + Dur("duration", duration). + Msg("") +})) +c = c.Append(hlog.RemoteAddrHandler("ip")) +c = c.Append(hlog.UserAgentHandler("user_agent")) +c = c.Append(hlog.RefererHandler("referer")) +c = c.Append(hlog.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, hlog.FromRequest + // will return a no-op logger. + hlog.FromRequest(r).Info(). + Str("user", "current user"). + Str("status", "ok"). + Msg("Something happened") + + // Output: {"level":"info","time":"2001-02-03T04:05:06Z","role":"my-service","host":"local-hostname","req_id":"b4g0l5t6tfid6dtrapu0","user":"current user","status":"ok","message":"Something happened"} +})) +http.Handle("/", h) + +if err := http.ListenAndServe(":8080", nil); err != nil { + log.Fatal().Err(err).Msg("Startup failed") +} +``` + +## Global Settings + +Some settings can be changed and will by applied to all loggers: + +* `log.Logger`: You can set this value to customize the global logger (the one used by package level methods). +* `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Set this to `zerolog.Disable` to disable logging altogether (quiet mode). +* `zerolog.DisableSampling`: If argument is `true`, all sampled loggers will stop sampling and issue 100% of their log events. +* `zerolog.TimestampFieldName`: Can be set to customize `Timestamp` field name. +* `zerolog.LevelFieldName`: Can be set to customize level field name. +* `zerolog.MessageFieldName`: Can be set to customize message field name. +* `zerolog.ErrorFieldName`: Can be set to customize `Err` field name. +* `zerolog.SampleFieldName`: Can be set to customize the field name added when sampling is enabled. +* `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with an empty string, times are formated as UNIX timestamp. + // DurationFieldUnit defines the unit for time.Duration type fields added + // using the Dur method. +* `DurationFieldUnit`: Sets the unit of the fields added by `Dur` (default: `time.Millisecond`). +* `DurationFieldInteger`: If set to true, `Dur` fields are formatted as integers instead of floats. + +## Field Types + +### Standard Types + +* `Str` +* `Bool` +* `Int`, `Int8`, `Int16`, `Int32`, `Int64` +* `Uint`, `Uint8`, `Uint16`, `Uint32`, `Uint64` +* `Float32`, `Float64` + +### Advanced Fields + +* `Err`: Takes an `error` and render it as a string using the `zerolog.ErrorFieldName` field name. +* `Timestamp`: Insert a timestamp field with `zerolog.TimestampFieldName` field name and formatted using `zerolog.TimeFieldFormat`. +* `Time`: Adds a field with the time formated with the `zerolog.TimeFieldFormat`. +* `Dur`: Adds a field with a `time.Duration`. +* `Dict`: Adds a sub-key/value as a field of the event. +* `Interface`: Uses reflection to marshal the type. + +## Performance + +All operations are allocation free (those numbers *include* JSON encoding): + +``` +BenchmarkLogEmpty-8 100000000 19.1 ns/op 0 B/op 0 allocs/op +BenchmarkDisabled-8 500000000 4.07 ns/op 0 B/op 0 allocs/op +BenchmarkInfo-8 30000000 42.5 ns/op 0 B/op 0 allocs/op +BenchmarkContextFields-8 30000000 44.9 ns/op 0 B/op 0 allocs/op +BenchmarkLogFields-8 10000000 184 ns/op 0 B/op 0 allocs/op +``` + +Using Uber's zap [comparison benchmark](https://github.com/uber-go/zap#performance): + +Log a message and 10 fields: + +| Library | Time | Bytes Allocated | Objects Allocated | +| :--- | :---: | :---: | :---: | +| zerolog | 767 ns/op | 552 B/op | 6 allocs/op | +| :zap: zap | 848 ns/op | 704 B/op | 2 allocs/op | +| :zap: zap (sugared) | 1363 ns/op | 1610 B/op | 20 allocs/op | +| go-kit | 3614 ns/op | 2895 B/op | 66 allocs/op | +| lion | 5392 ns/op | 5807 B/op | 63 allocs/op | +| logrus | 5661 ns/op | 6092 B/op | 78 allocs/op | +| apex/log | 15332 ns/op | 3832 B/op | 65 allocs/op | +| log15 | 20657 ns/op | 5632 B/op | 93 allocs/op | + +Log a message with a logger that already has 10 fields of context: + +| Library | Time | Bytes Allocated | Objects Allocated | +| :--- | :---: | :---: | :---: | +| zerolog | 52 ns/op | 0 B/op | 0 allocs/op | +| :zap: zap | 283 ns/op | 0 B/op | 0 allocs/op | +| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op | +| lion | 2702 ns/op | 4074 B/op | 38 allocs/op | +| go-kit | 3378 ns/op | 3046 B/op | 52 allocs/op | +| logrus | 4309 ns/op | 4564 B/op | 63 allocs/op | +| apex/log | 13456 ns/op | 2898 B/op | 51 allocs/op | +| log15 | 14179 ns/op | 2642 B/op | 44 allocs/op | + +Log a static string, without any context or `printf`-style templating: + +| Library | Time | Bytes Allocated | Objects Allocated | +| :--- | :---: | :---: | :---: | +| zerolog | 50 ns/op | 0 B/op | 0 allocs/op | +| :zap: zap | 236 ns/op | 0 B/op | 0 allocs/op | +| standard library | 453 ns/op | 80 B/op | 2 allocs/op | +| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op | +| go-kit | 508 ns/op | 656 B/op | 13 allocs/op | +| lion | 771 ns/op | 1224 B/op | 10 allocs/op | +| logrus | 1244 ns/op | 1505 B/op | 27 allocs/op | +| apex/log | 2751 ns/op | 584 B/op | 11 allocs/op | +| log15 | 5181 ns/op | 1592 B/op | 26 allocs/op | + diff --git a/vendor/github.com/rs/zerolog/array.go b/vendor/github.com/rs/zerolog/array.go new file mode 100644 index 000000000..ca17b8e71 --- /dev/null +++ b/vendor/github.com/rs/zerolog/array.go @@ -0,0 +1,169 @@ +package zerolog + +import ( + "sync" + "time" + + "github.com/rs/zerolog/internal/json" +) + +var arrayPool = &sync.Pool{ + New: func() interface{} { + return &Array{ + buf: make([]byte, 0, 500), + } + }, +} + +type Array struct { + buf []byte +} + +// Arr creates an array to be added to an Event or Context. +func Arr() *Array { + a := arrayPool.Get().(*Array) + a.buf = a.buf[:0] + return a +} + +func (*Array) MarshalZerologArray(*Array) { +} + +func (a *Array) write(dst []byte) []byte { + if len(a.buf) == 0 { + dst = append(dst, `[]`...) + } else { + a.buf[0] = '[' + dst = append(append(dst, a.buf...), ']') + } + arrayPool.Put(a) + return dst +} + +// Object marshals an object that implement the LogObjectMarshaler +// interface and append it to the array. +func (a *Array) Object(obj LogObjectMarshaler) *Array { + a.buf = append(a.buf, ',') + e := Dict() + obj.MarshalZerologObject(e) + e.buf = append(e.buf, '}') + a.buf = append(a.buf, e.buf...) + return a +} + +// Str append the val as a string to the array. +func (a *Array) Str(val string) *Array { + a.buf = json.AppendString(append(a.buf, ','), val) + return a +} + +// Bytes append the val as a string to the array. +func (a *Array) Bytes(val []byte) *Array { + a.buf = json.AppendBytes(append(a.buf, ','), val) + return a +} + +// Err append the err as a string to the array. +func (a *Array) Err(err error) *Array { + a.buf = json.AppendError(append(a.buf, ','), err) + return a +} + +// Bool append the val as a bool to the array. +func (a *Array) Bool(b bool) *Array { + a.buf = json.AppendBool(append(a.buf, ','), b) + return a +} + +// Int append i as a int to the array. +func (a *Array) Int(i int) *Array { + a.buf = json.AppendInt(append(a.buf, ','), i) + return a +} + +// Int8 append i as a int8 to the array. +func (a *Array) Int8(i int8) *Array { + a.buf = json.AppendInt8(append(a.buf, ','), i) + return a +} + +// Int16 append i as a int16 to the array. +func (a *Array) Int16(i int16) *Array { + a.buf = json.AppendInt16(append(a.buf, ','), i) + return a +} + +// Int32 append i as a int32 to the array. +func (a *Array) Int32(i int32) *Array { + a.buf = json.AppendInt32(append(a.buf, ','), i) + return a +} + +// Int64 append i as a int64 to the array. +func (a *Array) Int64(i int64) *Array { + a.buf = json.AppendInt64(append(a.buf, ','), i) + return a +} + +// Uint append i as a uint to the array. +func (a *Array) Uint(i uint) *Array { + a.buf = json.AppendUint(append(a.buf, ','), i) + return a +} + +// Uint8 append i as a uint8 to the array. +func (a *Array) Uint8(i uint8) *Array { + a.buf = json.AppendUint8(append(a.buf, ','), i) + return a +} + +// Uint16 append i as a uint16 to the array. +func (a *Array) Uint16(i uint16) *Array { + a.buf = json.AppendUint16(append(a.buf, ','), i) + return a +} + +// Uint32 append i as a uint32 to the array. +func (a *Array) Uint32(i uint32) *Array { + a.buf = json.AppendUint32(append(a.buf, ','), i) + return a +} + +// Uint64 append i as a uint64 to the array. +func (a *Array) Uint64(i uint64) *Array { + a.buf = json.AppendUint64(append(a.buf, ','), i) + return a +} + +// Float32 append f as a float32 to the array. +func (a *Array) Float32(f float32) *Array { + a.buf = json.AppendFloat32(append(a.buf, ','), f) + return a +} + +// Float64 append f as a float64 to the array. +func (a *Array) Float64(f float64) *Array { + a.buf = json.AppendFloat64(append(a.buf, ','), f) + return a +} + +// Time append t formated as string using zerolog.TimeFieldFormat. +func (a *Array) Time(t time.Time) *Array { + a.buf = json.AppendTime(append(a.buf, ','), t, TimeFieldFormat) + return a +} + +// Dur append d to the array. +func (a *Array) Dur(d time.Duration) *Array { + a.buf = json.AppendDuration(append(a.buf, ','), d, DurationFieldUnit, DurationFieldInteger) + return a +} + +// Interface append i marshaled using reflection. +func (a *Array) Interface(i interface{}) *Array { + if obj, ok := i.(LogObjectMarshaler); ok { + return a.Object(obj) + } + a.buf = json.AppendInterface(append(a.buf, ','), i) + return a +} diff --git a/vendor/github.com/rs/zerolog/context.go b/vendor/github.com/rs/zerolog/context.go new file mode 100644 index 000000000..cd25687cd --- /dev/null +++ b/vendor/github.com/rs/zerolog/context.go @@ -0,0 +1,300 @@ +package zerolog + +import ( + "io/ioutil" + "time" + + "github.com/rs/zerolog/internal/json" +) + +// Context configures a new sub-logger with contextual fields. +type Context struct { + l Logger +} + +// Logger returns the logger with the context previously set. +func (c Context) Logger() Logger { + return c.l +} + +// Fields is a helper function to use a map to set fields using type assertion. +func (c Context) Fields(fields map[string]interface{}) Context { + c.l.context = appendFields(c.l.context, fields) + return c +} + +// Dict adds the field key with the dict to the logger context. +func (c Context) Dict(key string, dict *Event) Context { + dict.buf = append(dict.buf, '}') + c.l.context = append(json.AppendKey(c.l.context, key), dict.buf...) + eventPool.Put(dict) + return c +} + +// Array adds the field key with an array to the event context. +// Use zerolog.Arr() to create the array or pass a type that +// implement the LogArrayMarshaler interface. +func (c Context) Array(key string, arr LogArrayMarshaler) Context { + c.l.context = json.AppendKey(c.l.context, key) + if arr, ok := arr.(*Array); ok { + c.l.context = arr.write(c.l.context) + return c + } + var a *Array + if aa, ok := arr.(*Array); ok { + a = aa + } else { + a = Arr() + arr.MarshalZerologArray(a) + } + c.l.context = a.write(c.l.context) + return c +} + +// Object marshals an object that implement the LogObjectMarshaler interface. +func (c Context) Object(key string, obj LogObjectMarshaler) Context { + e := newEvent(levelWriterAdapter{ioutil.Discard}, 0, true) + e.Object(key, obj) + e.buf[0] = ',' // A new event starts as an object, we want to embed it. + c.l.context = append(c.l.context, e.buf...) + eventPool.Put(e) + return c +} + +// Str adds the field key with val as a string to the logger context. +func (c Context) Str(key, val string) Context { + c.l.context = json.AppendString(json.AppendKey(c.l.context, key), val) + return c +} + +// Strs adds the field key with val as a string to the logger context. +func (c Context) Strs(key string, vals []string) Context { + c.l.context = json.AppendStrings(json.AppendKey(c.l.context, key), vals) + return c +} + +// Bytes adds the field key with val as a []byte to the logger context. +func (c Context) Bytes(key string, val []byte) Context { + c.l.context = json.AppendBytes(json.AppendKey(c.l.context, key), val) + return c +} + +// AnErr adds the field key with err as a string to the logger context. +func (c Context) AnErr(key string, err error) Context { + if err != nil { + c.l.context = json.AppendError(json.AppendKey(c.l.context, key), err) + } + return c +} + +// Errs adds the field key with errs as an array of strings to the logger context. +func (c Context) Errs(key string, errs []error) Context { + c.l.context = json.AppendErrors(json.AppendKey(c.l.context, key), errs) + return c +} + +// Err adds the field "error" with err as a string to the logger context. +// To customize the key name, change zerolog.ErrorFieldName. +func (c Context) Err(err error) Context { + if err != nil { + c.l.context = json.AppendError(json.AppendKey(c.l.context, ErrorFieldName), err) + } + return c +} + +// Bool adds the field key with val as a bool to the logger context. +func (c Context) Bool(key string, b bool) Context { + c.l.context = json.AppendBool(json.AppendKey(c.l.context, key), b) + return c +} + +// Bools adds the field key with val as a []bool to the logger context. +func (c Context) Bools(key string, b []bool) Context { + c.l.context = json.AppendBools(json.AppendKey(c.l.context, key), b) + return c +} + +// Int adds the field key with i as a int to the logger context. +func (c Context) Int(key string, i int) Context { + c.l.context = json.AppendInt(json.AppendKey(c.l.context, key), i) + return c +} + +// Ints adds the field key with i as a []int to the logger context. +func (c Context) Ints(key string, i []int) Context { + c.l.context = json.AppendInts(json.AppendKey(c.l.context, key), i) + return c +} + +// Int8 adds the field key with i as a int8 to the logger context. +func (c Context) Int8(key string, i int8) Context { + c.l.context = json.AppendInt8(json.AppendKey(c.l.context, key), i) + return c +} + +// Ints8 adds the field key with i as a []int8 to the logger context. +func (c Context) Ints8(key string, i []int8) Context { + c.l.context = json.AppendInts8(json.AppendKey(c.l.context, key), i) + return c +} + +// Int16 adds the field key with i as a int16 to the logger context. +func (c Context) Int16(key string, i int16) Context { + c.l.context = json.AppendInt16(json.AppendKey(c.l.context, key), i) + return c +} + +// Ints16 adds the field key with i as a []int16 to the logger context. +func (c Context) Ints16(key string, i []int16) Context { + c.l.context = json.AppendInts16(json.AppendKey(c.l.context, key), i) + return c +} + +// Int32 adds the field key with i as a int32 to the logger context. +func (c Context) Int32(key string, i int32) Context { + c.l.context = json.AppendInt32(json.AppendKey(c.l.context, key), i) + return c +} + +// Ints32 adds the field key with i as a []int32 to the logger context. +func (c Context) Ints32(key string, i []int32) Context { + c.l.context = json.AppendInts32(json.AppendKey(c.l.context, key), i) + return c +} + +// Int64 adds the field key with i as a int64 to the logger context. +func (c Context) Int64(key string, i int64) Context { + c.l.context = json.AppendInt64(json.AppendKey(c.l.context, key), i) + return c +} + +// Ints64 adds the field key with i as a []int64 to the logger context. +func (c Context) Ints64(key string, i []int64) Context { + c.l.context = json.AppendInts64(json.AppendKey(c.l.context, key), i) + return c +} + +// Uint adds the field key with i as a uint to the logger context. +func (c Context) Uint(key string, i uint) Context { + c.l.context = json.AppendUint(json.AppendKey(c.l.context, key), i) + return c +} + +// Uints adds the field key with i as a []uint to the logger context. +func (c Context) Uints(key string, i []uint) Context { + c.l.context = json.AppendUints(json.AppendKey(c.l.context, key), i) + return c +} + +// Uint8 adds the field key with i as a uint8 to the logger context. +func (c Context) Uint8(key string, i uint8) Context { + c.l.context = json.AppendUint8(json.AppendKey(c.l.context, key), i) + return c +} + +// Uints8 adds the field key with i as a []uint8 to the logger context. +func (c Context) Uints8(key string, i []uint8) Context { + c.l.context = json.AppendUints8(json.AppendKey(c.l.context, key), i) + return c +} + +// Uint16 adds the field key with i as a uint16 to the logger context. +func (c Context) Uint16(key string, i uint16) Context { + c.l.context = json.AppendUint16(json.AppendKey(c.l.context, key), i) + return c +} + +// Uints16 adds the field key with i as a []uint16 to the logger context. +func (c Context) Uints16(key string, i []uint16) Context { + c.l.context = json.AppendUints16(json.AppendKey(c.l.context, key), i) + return c +} + +// Uint32 adds the field key with i as a uint32 to the logger context. +func (c Context) Uint32(key string, i uint32) Context { + c.l.context = json.AppendUint32(json.AppendKey(c.l.context, key), i) + return c +} + +// Uints32 adds the field key with i as a []uint32 to the logger context. +func (c Context) Uints32(key string, i []uint32) Context { + c.l.context = json.AppendUints32(json.AppendKey(c.l.context, key), i) + return c +} + +// Uint64 adds the field key with i as a uint64 to the logger context. +func (c Context) Uint64(key string, i uint64) Context { + c.l.context = json.AppendUint64(json.AppendKey(c.l.context, key), i) + return c +} + +// Uints64 adds the field key with i as a []uint64 to the logger context. +func (c Context) Uints64(key string, i []uint64) Context { + c.l.context = json.AppendUints64(json.AppendKey(c.l.context, key), i) + return c +} + +// Float32 adds the field key with f as a float32 to the logger context. +func (c Context) Float32(key string, f float32) Context { + c.l.context = json.AppendFloat32(json.AppendKey(c.l.context, key), f) + return c +} + +// Floats32 adds the field key with f as a []float32 to the logger context. +func (c Context) Floats32(key string, f []float32) Context { + c.l.context = json.AppendFloats32(json.AppendKey(c.l.context, key), f) + return c +} + +// Float64 adds the field key with f as a float64 to the logger context. +func (c Context) Float64(key string, f float64) Context { + c.l.context = json.AppendFloat64(json.AppendKey(c.l.context, key), f) + return c +} + +// Floats64 adds the field key with f as a []float64 to the logger context. +func (c Context) Floats64(key string, f []float64) Context { + c.l.context = json.AppendFloats64(json.AppendKey(c.l.context, key), f) + return c +} + +// Timestamp adds the current local time as UNIX timestamp to the logger context with the "time" key. +// To customize the key name, change zerolog.TimestampFieldName. +func (c Context) Timestamp() Context { + if len(c.l.context) > 0 { + c.l.context[0] = 1 + } else { + c.l.context = append(c.l.context, 1) + } + return c +} + +// Time adds the field key with t formated as string using zerolog.TimeFieldFormat. +func (c Context) Time(key string, t time.Time) Context { + c.l.context = json.AppendTime(json.AppendKey(c.l.context, key), t, TimeFieldFormat) + return c +} + +// Times adds the field key with t formated as string using zerolog.TimeFieldFormat. +func (c Context) Times(key string, t []time.Time) Context { + c.l.context = json.AppendTimes(json.AppendKey(c.l.context, key), t, TimeFieldFormat) + return c +} + +// Dur adds the fields key with d divided by unit and stored as a float. +func (c Context) Dur(key string, d time.Duration) Context { + c.l.context = json.AppendDuration(json.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger) + return c +} + +// Durs adds the fields key with d divided by unit and stored as a float. +func (c Context) Durs(key string, d []time.Duration) Context { + c.l.context = json.AppendDurations(json.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger) + return c +} + +// Interface adds the field key with obj marshaled using reflection. +func (c Context) Interface(key string, i interface{}) Context { + c.l.context = json.AppendInterface(json.AppendKey(c.l.context, key), i) + return c +} diff --git a/vendor/github.com/rs/zerolog/ctx.go b/vendor/github.com/rs/zerolog/ctx.go new file mode 100644 index 000000000..034eccdde --- /dev/null +++ b/vendor/github.com/rs/zerolog/ctx.go @@ -0,0 +1,29 @@ +package zerolog + +import ( + "context" + "io/ioutil" +) + +var disabledLogger = New(ioutil.Discard).Level(Disabled) + +type ctxKey struct{} + +// WithContext returns a copy of ctx with l associated. +func (l Logger) WithContext(ctx context.Context) context.Context { + if lp, ok := ctx.Value(ctxKey{}).(*Logger); ok { + // Update existing pointer. + *lp = l + return ctx + } + return context.WithValue(ctx, ctxKey{}, &l) +} + +// Ctx returns the Logger associated with the ctx. If no logger +// is associated, a disabled logger is returned. +func Ctx(ctx context.Context) Logger { + if l, ok := ctx.Value(ctxKey{}).(*Logger); ok { + return *l + } + return disabledLogger +} diff --git a/vendor/github.com/rs/zerolog/event.go b/vendor/github.com/rs/zerolog/event.go new file mode 100644 index 000000000..290b01d4d --- /dev/null +++ b/vendor/github.com/rs/zerolog/event.go @@ -0,0 +1,555 @@ +package zerolog + +import ( + "fmt" + "io/ioutil" + "os" + "sync" + "time" + + "github.com/rs/zerolog/internal/json" +) + +var eventPool = &sync.Pool{ + New: func() interface{} { + return &Event{ + buf: make([]byte, 0, 500), + } + }, +} + +// Event represents a log event. It is instanced by one of the level method of +// Logger and finalized by the Msg or Msgf method. +type Event struct { + buf []byte + w LevelWriter + level Level + enabled bool + done func(msg string) +} + +// LogObjectMarshaler provides a strongly-typed and encoding-agnostic interface +// to be implemented by types used with Event/Context's Object methods. +type LogObjectMarshaler interface { + MarshalZerologObject(e *Event) +} + +// LogArrayMarshaler provides a strongly-typed and encoding-agnostic interface +// to be implemented by types used with Event/Context's Array methods. +type LogArrayMarshaler interface { + MarshalZerologArray(a *Array) +} + +func newEvent(w LevelWriter, level Level, enabled bool) *Event { + if !enabled { + return &Event{} + } + e := eventPool.Get().(*Event) + e.buf = e.buf[:1] + e.buf[0] = '{' + e.w = w + e.level = level + e.enabled = true + return e +} + +func (e *Event) write() (err error) { + if !e.enabled { + return nil + } + e.buf = append(e.buf, '}', '\n') + _, err = e.w.WriteLevel(e.level, e.buf) + eventPool.Put(e) + return +} + +// Enabled return false if the *Event is going to be filtered out by +// log level or sampling. +func (e *Event) Enabled() bool { + return e.enabled +} + +// Msg sends the *Event with msg added as the message field if not empty. +// +// NOTICE: once this method is called, the *Event should be disposed. +// Calling Msg twice can have unexpected result. +func (e *Event) Msg(msg string) { + if !e.enabled { + return + } + if msg != "" { + e.buf = json.AppendString(json.AppendKey(e.buf, MessageFieldName), msg) + } + if e.done != nil { + defer e.done(msg) + } + if err := e.write(); err != nil { + fmt.Fprintf(os.Stderr, "zerolog: could not write event: %v", err) + } +} + +// Msgf sends the event with formated msg added as the message field if not empty. +// +// NOTICE: once this methid is called, the *Event should be disposed. +// Calling Msg twice can have unexpected result. +func (e *Event) Msgf(format string, v ...interface{}) { + if !e.enabled { + return + } + msg := fmt.Sprintf(format, v...) + if msg != "" { + e.buf = json.AppendString(json.AppendKey(e.buf, MessageFieldName), msg) + } + if e.done != nil { + defer e.done(msg) + } + if err := e.write(); err != nil { + fmt.Fprintf(os.Stderr, "zerolog: could not write event: %v", err) + } +} + +// Fields is a helper function to use a map to set fields using type assertion. +func (e *Event) Fields(fields map[string]interface{}) *Event { + if !e.enabled { + return e + } + e.buf = appendFields(e.buf, fields) + return e +} + +// Dict adds the field key with a dict to the event context. +// Use zerolog.Dict() to create the dictionary. +func (e *Event) Dict(key string, dict *Event) *Event { + if !e.enabled { + return e + } + e.buf = append(append(json.AppendKey(e.buf, key), dict.buf...), '}') + eventPool.Put(dict) + return e +} + +// Dict creates an Event to be used with the *Event.Dict method. +// Call usual field methods like Str, Int etc to add fields to this +// event and give it as argument the *Event.Dict method. +func Dict() *Event { + return newEvent(levelWriterAdapter{ioutil.Discard}, 0, true) +} + +// Array adds the field key with an array to the event context. +// Use zerolog.Arr() to create the array or pass a type that +// implement the LogArrayMarshaler interface. +func (e *Event) Array(key string, arr LogArrayMarshaler) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendKey(e.buf, key) + var a *Array + if aa, ok := arr.(*Array); ok { + a = aa + } else { + a = Arr() + arr.MarshalZerologArray(a) + } + e.buf = a.write(e.buf) + return e +} + +func (e *Event) appendObject(obj LogObjectMarshaler) { + pos := len(e.buf) + obj.MarshalZerologObject(e) + if pos < len(e.buf) { + // As MarshalZerologObject will use event API, the first field will be + // preceded by a comma. If at least one field has been added (buf grew), + // we replace this coma by the opening bracket. + e.buf[pos] = '{' + } else { + e.buf = append(e.buf, '{') + } + e.buf = append(e.buf, '}') +} + +// Object marshals an object that implement the LogObjectMarshaler interface. +func (e *Event) Object(key string, obj LogObjectMarshaler) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendKey(e.buf, key) + e.appendObject(obj) + return e +} + +// Str adds the field key with val as a string to the *Event context. +func (e *Event) Str(key, val string) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendString(json.AppendKey(e.buf, key), val) + return e +} + +// Strs adds the field key with vals as a []string to the *Event context. +func (e *Event) Strs(key string, vals []string) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendStrings(json.AppendKey(e.buf, key), vals) + return e +} + +// Bytes adds the field key with val as a string to the *Event context. +// +// Runes outside of normal ASCII ranges will be hex-encoded in the resulting +// JSON. +func (e *Event) Bytes(key string, val []byte) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendBytes(json.AppendKey(e.buf, key), val) + return e +} + +// AnErr adds the field key with err as a string to the *Event context. +// If err is nil, no field is added. +func (e *Event) AnErr(key string, err error) *Event { + if !e.enabled { + return e + } + if err != nil { + e.buf = json.AppendError(json.AppendKey(e.buf, key), err) + } + return e +} + +// Errs adds the field key with errs as an array of strings to the *Event context. +// If err is nil, no field is added. +func (e *Event) Errs(key string, errs []error) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendErrors(json.AppendKey(e.buf, key), errs) + return e +} + +// Err adds the field "error" with err as a string to the *Event context. +// If err is nil, no field is added. +// To customize the key name, change zerolog.ErrorFieldName. +func (e *Event) Err(err error) *Event { + if !e.enabled { + return e + } + if err != nil { + e.buf = json.AppendError(json.AppendKey(e.buf, ErrorFieldName), err) + } + return e +} + +// Bool adds the field key with val as a bool to the *Event context. +func (e *Event) Bool(key string, b bool) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendBool(json.AppendKey(e.buf, key), b) + return e +} + +// Bools adds the field key with val as a []bool to the *Event context. +func (e *Event) Bools(key string, b []bool) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendBools(json.AppendKey(e.buf, key), b) + return e +} + +// Int adds the field key with i as a int to the *Event context. +func (e *Event) Int(key string, i int) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendInt(json.AppendKey(e.buf, key), i) + return e +} + +// Ints adds the field key with i as a []int to the *Event context. +func (e *Event) Ints(key string, i []int) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendInts(json.AppendKey(e.buf, key), i) + return e +} + +// Int8 adds the field key with i as a int8 to the *Event context. +func (e *Event) Int8(key string, i int8) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendInt8(json.AppendKey(e.buf, key), i) + return e +} + +// Ints8 adds the field key with i as a []int8 to the *Event context. +func (e *Event) Ints8(key string, i []int8) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendInts8(json.AppendKey(e.buf, key), i) + return e +} + +// Int16 adds the field key with i as a int16 to the *Event context. +func (e *Event) Int16(key string, i int16) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendInt16(json.AppendKey(e.buf, key), i) + return e +} + +// Ints16 adds the field key with i as a []int16 to the *Event context. +func (e *Event) Ints16(key string, i []int16) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendInts16(json.AppendKey(e.buf, key), i) + return e +} + +// Int32 adds the field key with i as a int32 to the *Event context. +func (e *Event) Int32(key string, i int32) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendInt32(json.AppendKey(e.buf, key), i) + return e +} + +// Ints32 adds the field key with i as a []int32 to the *Event context. +func (e *Event) Ints32(key string, i []int32) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendInts32(json.AppendKey(e.buf, key), i) + return e +} + +// Int64 adds the field key with i as a int64 to the *Event context. +func (e *Event) Int64(key string, i int64) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendInt64(json.AppendKey(e.buf, key), i) + return e +} + +// Ints64 adds the field key with i as a []int64 to the *Event context. +func (e *Event) Ints64(key string, i []int64) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendInts64(json.AppendKey(e.buf, key), i) + return e +} + +// Uint adds the field key with i as a uint to the *Event context. +func (e *Event) Uint(key string, i uint) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendUint(json.AppendKey(e.buf, key), i) + return e +} + +// Uints adds the field key with i as a []int to the *Event context. +func (e *Event) Uints(key string, i []uint) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendUints(json.AppendKey(e.buf, key), i) + return e +} + +// Uint8 adds the field key with i as a uint8 to the *Event context. +func (e *Event) Uint8(key string, i uint8) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendUint8(json.AppendKey(e.buf, key), i) + return e +} + +// Uints8 adds the field key with i as a []int8 to the *Event context. +func (e *Event) Uints8(key string, i []uint8) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendUints8(json.AppendKey(e.buf, key), i) + return e +} + +// Uint16 adds the field key with i as a uint16 to the *Event context. +func (e *Event) Uint16(key string, i uint16) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendUint16(json.AppendKey(e.buf, key), i) + return e +} + +// Uints16 adds the field key with i as a []int16 to the *Event context. +func (e *Event) Uints16(key string, i []uint16) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendUints16(json.AppendKey(e.buf, key), i) + return e +} + +// Uint32 adds the field key with i as a uint32 to the *Event context. +func (e *Event) Uint32(key string, i uint32) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendUint32(json.AppendKey(e.buf, key), i) + return e +} + +// Uints32 adds the field key with i as a []int32 to the *Event context. +func (e *Event) Uints32(key string, i []uint32) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendUints32(json.AppendKey(e.buf, key), i) + return e +} + +// Uint64 adds the field key with i as a uint64 to the *Event context. +func (e *Event) Uint64(key string, i uint64) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendUint64(json.AppendKey(e.buf, key), i) + return e +} + +// Uints64 adds the field key with i as a []int64 to the *Event context. +func (e *Event) Uints64(key string, i []uint64) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendUints64(json.AppendKey(e.buf, key), i) + return e +} + +// Float32 adds the field key with f as a float32 to the *Event context. +func (e *Event) Float32(key string, f float32) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendFloat32(json.AppendKey(e.buf, key), f) + return e +} + +// Floats32 adds the field key with f as a []float32 to the *Event context. +func (e *Event) Floats32(key string, f []float32) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendFloats32(json.AppendKey(e.buf, key), f) + return e +} + +// Float64 adds the field key with f as a float64 to the *Event context. +func (e *Event) Float64(key string, f float64) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendFloat64(json.AppendKey(e.buf, key), f) + return e +} + +// Floats64 adds the field key with f as a []float64 to the *Event context. +func (e *Event) Floats64(key string, f []float64) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendFloats64(json.AppendKey(e.buf, key), f) + return e +} + +// Timestamp adds the current local time as UNIX timestamp to the *Event context with the "time" key. +// To customize the key name, change zerolog.TimestampFieldName. +func (e *Event) Timestamp() *Event { + if !e.enabled { + return e + } + e.buf = json.AppendTime(json.AppendKey(e.buf, TimestampFieldName), TimestampFunc(), TimeFieldFormat) + return e +} + +// Time adds the field key with t formated as string using zerolog.TimeFieldFormat. +func (e *Event) Time(key string, t time.Time) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendTime(json.AppendKey(e.buf, key), t, TimeFieldFormat) + return e +} + +// Times adds the field key with t formated as string using zerolog.TimeFieldFormat. +func (e *Event) Times(key string, t []time.Time) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendTimes(json.AppendKey(e.buf, key), t, TimeFieldFormat) + return e +} + +// Dur adds the field key with duration d stored as zerolog.DurationFieldUnit. +// If zerolog.DurationFieldInteger is true, durations are rendered as integer +// instead of float. +func (e *Event) Dur(key string, d time.Duration) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendDuration(json.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger) + return e +} + +// Durs adds the field key with duration d stored as zerolog.DurationFieldUnit. +// If zerolog.DurationFieldInteger is true, durations are rendered as integer +// instead of float. +func (e *Event) Durs(key string, d []time.Duration) *Event { + if !e.enabled { + return e + } + e.buf = json.AppendDurations(json.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger) + return e +} + +// TimeDiff adds the field key with positive duration between time t and start. +// If time t is not greater than start, duration will be 0. +// Duration format follows the same principle as Dur(). +func (e *Event) TimeDiff(key string, t time.Time, start time.Time) *Event { + if !e.enabled { + return e + } + var d time.Duration + if t.After(start) { + d = t.Sub(start) + } + e.buf = json.AppendDuration(json.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger) + return e +} + +// Interface adds the field key with i marshaled using reflection. +func (e *Event) Interface(key string, i interface{}) *Event { + if !e.enabled { + return e + } + if obj, ok := i.(LogObjectMarshaler); ok { + return e.Object(key, obj) + } + e.buf = json.AppendInterface(json.AppendKey(e.buf, key), i) + return e +} diff --git a/vendor/github.com/rs/zerolog/fields.go b/vendor/github.com/rs/zerolog/fields.go new file mode 100644 index 000000000..6a19392b7 --- /dev/null +++ b/vendor/github.com/rs/zerolog/fields.go @@ -0,0 +1,96 @@ +package zerolog + +import ( + "sort" + "time" + + "github.com/rs/zerolog/internal/json" +) + +func appendFields(dst []byte, fields map[string]interface{}) []byte { + keys := make([]string, 0, len(fields)) + for key := range fields { + keys = append(keys, key) + } + sort.Strings(keys) + for _, key := range keys { + dst = json.AppendKey(dst, key) + switch val := fields[key].(type) { + case string: + dst = json.AppendString(dst, val) + case []byte: + dst = json.AppendBytes(dst, val) + case error: + dst = json.AppendError(dst, val) + case []error: + dst = json.AppendErrors(dst, val) + case bool: + dst = json.AppendBool(dst, val) + case int: + dst = json.AppendInt(dst, val) + case int8: + dst = json.AppendInt8(dst, val) + case int16: + dst = json.AppendInt16(dst, val) + case int32: + dst = json.AppendInt32(dst, val) + case int64: + dst = json.AppendInt64(dst, val) + case uint: + dst = json.AppendUint(dst, val) + case uint8: + dst = json.AppendUint8(dst, val) + case uint16: + dst = json.AppendUint16(dst, val) + case uint32: + dst = json.AppendUint32(dst, val) + case uint64: + dst = json.AppendUint64(dst, val) + case float32: + dst = json.AppendFloat32(dst, val) + case float64: + dst = json.AppendFloat64(dst, val) + case time.Time: + dst = json.AppendTime(dst, val, TimeFieldFormat) + case time.Duration: + dst = json.AppendDuration(dst, val, DurationFieldUnit, DurationFieldInteger) + case []string: + dst = json.AppendStrings(dst, val) + case []bool: + dst = json.AppendBools(dst, val) + case []int: + dst = json.AppendInts(dst, val) + case []int8: + dst = json.AppendInts8(dst, val) + case []int16: + dst = json.AppendInts16(dst, val) + case []int32: + dst = json.AppendInts32(dst, val) + case []int64: + dst = json.AppendInts64(dst, val) + case []uint: + dst = json.AppendUints(dst, val) + // case []uint8: + // dst = appendUints8(dst, val) + case []uint16: + dst = json.AppendUints16(dst, val) + case []uint32: + dst = json.AppendUints32(dst, val) + case []uint64: + dst = json.AppendUints64(dst, val) + case []float32: + dst = json.AppendFloats32(dst, val) + case []float64: + dst = json.AppendFloats64(dst, val) + case []time.Time: + dst = json.AppendTimes(dst, val, TimeFieldFormat) + case []time.Duration: + dst = json.AppendDurations(dst, val, DurationFieldUnit, DurationFieldInteger) + case nil: + dst = append(dst, "null"...) + default: + dst = json.AppendInterface(dst, val) + } + } + return dst +} diff --git a/vendor/github.com/rs/zerolog/globals.go b/vendor/github.com/rs/zerolog/globals.go new file mode 100644 index 000000000..acc80fbc9 --- /dev/null +++ b/vendor/github.com/rs/zerolog/globals.go @@ -0,0 +1,67 @@ +package zerolog + +import "time" +import "sync/atomic" + +var ( + // TimestampFieldName is the field name used for the timestamp field. + TimestampFieldName = "time" + + // LevelFieldName is the field name used for the level field. + LevelFieldName = "level" + + // MessageFieldName is the field name used for the message field. + MessageFieldName = "message" + + // ErrorFieldName is the field name used for error fields. + ErrorFieldName = "error" + + // SampleFieldName is the name of the field used to report sampling. + SampleFieldName = "sample" + + // TimeFieldFormat defines the time format of the Time field type. + // If set to an empty string, the time is formatted as an UNIX timestamp + // as integer. + TimeFieldFormat = time.RFC3339 + + // TimestampFunc defines the function called to generate a timestamp. + TimestampFunc = time.Now + + // DurationFieldUnit defines the unit for time.Duration type fields added + // using the Dur method. + DurationFieldUnit = time.Millisecond + + // DurationFieldInteger renders Dur fields as integer instead of float if + // set to true. + DurationFieldInteger = false +) + +var ( + gLevel = new(uint32) + disableSampling = new(uint32) +) + +// SetGlobalLevel sets the global override for log level. If this +// values is raised, all Loggers will use at least this value. +// +// To globally disable logs, set GlobalLevel to Disabled. +func SetGlobalLevel(l Level) { + atomic.StoreUint32(gLevel, uint32(l)) +} + +func globalLevel() Level { + return Level(atomic.LoadUint32(gLevel)) +} + +// DisableSampling will disable sampling in all Loggers if true. +func DisableSampling(v bool) { + var i uint32 + if v { + i = 1 + } + atomic.StoreUint32(disableSampling, i) +} + +func samplingDisabled() bool { + return atomic.LoadUint32(disableSampling) == 1 +} diff --git a/vendor/github.com/rs/zerolog/internal/json/base.go b/vendor/github.com/rs/zerolog/internal/json/base.go new file mode 100644 index 000000000..7baeec5ed --- /dev/null +++ b/vendor/github.com/rs/zerolog/internal/json/base.go @@ -0,0 +1,39 @@ +package json + +func AppendKey(dst []byte, key string) []byte { + if len(dst) > 1 { + dst = append(dst, ',') + } + dst = AppendString(dst, key) + return append(dst, ':') +} + +func AppendError(dst []byte, err error) []byte { + if err == nil { + return append(dst, `null`...) + } + return AppendString(dst, err.Error()) +} + +func AppendErrors(dst []byte, errs []error) []byte { + if len(errs) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + if errs[0] != nil { + dst = AppendString(dst, errs[0].Error()) + } else { + dst = append(dst, "null"...) + } + if len(errs) > 1 { + for _, err := range errs[1:] { + if err == nil { + dst = append(dst, ",null"...) + continue + } + dst = AppendString(append(dst, ','), err.Error()) + } + } + dst = append(dst, ']') + return dst +} diff --git a/vendor/github.com/rs/zerolog/internal/json/string.go b/vendor/github.com/rs/zerolog/internal/json/string.go new file mode 100644 index 000000000..8f8f4df0d --- /dev/null +++ b/vendor/github.com/rs/zerolog/internal/json/string.go @@ -0,0 +1,180 @@ +package json + +import "unicode/utf8" + +const hex = "0123456789abcdef" + +func AppendStrings(dst []byte, vals []string) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = AppendString(dst, vals[0]) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = AppendString(append(dst, ','), val) + } + } + dst = append(dst, ']') + return dst +} + +// AppendString encodes the input string to json and appends +// the encoded string to the input byte slice. +// +// The operation loops though each byte in the string looking +// for characters that need json or utf8 encoding. If the string +// does not need encoding, then the string is appended in it's +// entirety to the byte slice. +// If we encounter a byte that does need encoding, switch up +// the operation and perform a byte-by-byte read-encode-append. +func AppendString(dst []byte, s string) []byte { + // Start with a double quote. + dst = append(dst, '"') + // Loop through each character in the string. + for i := 0; i < len(s); i++ { + // Check if the character needs encoding. Control characters, slashes, + // and the double quote need json encoding. Bytes above the ascii + // boundary needs utf8 encoding. + if s[i] < 0x20 || s[i] > 0x7e || s[i] == '\\' || s[i] == '"' { + // We encountered a character that needs to be encoded. Switch + // to complex version of the algorithm. + dst = appendStringComplex(dst, s, i) + return append(dst, '"') + } + } + // The string has no need for encoding an therefore is directly + // appended to the byte slice. + dst = append(dst, s...) + // End with a double quote + return append(dst, '"') +} + +// appendStringComplex is used by appendString to take over an in +// progress JSON string encoding that encountered a character that needs +// to be encoded. +func appendStringComplex(dst []byte, s string, i int) []byte { + start := 0 + for i < len(s) { + b := s[i] + if b >= utf8.RuneSelf { + r, size := utf8.DecodeRuneInString(s[i:]) + if r == utf8.RuneError && size == 1 { + // In case of error, first append previous simple characters to + // the byte slice if any and append a remplacement character code + // in place of the invalid sequence. + if start < i { + dst = append(dst, s[start:i]...) + } + dst = append(dst, `\ufffd`...) + i += size + start = i + continue + } + i += size + continue + } + if b >= 0x20 && b <= 0x7e && b != '\\' && b != '"' { + i++ + continue + } + // We encountered a character that needs to be encoded. + // Let's append the previous simple characters to the byte slice + // and switch our operation to read and encode the remainder + // characters byte-by-byte. + if start < i { + dst = append(dst, s[start:i]...) + } + switch b { + case '"', '\\': + dst = append(dst, '\\', b) + case '\b': + dst = append(dst, '\\', 'b') + case '\f': + dst = append(dst, '\\', 'f') + case '\n': + dst = append(dst, '\\', 'n') + case '\r': + dst = append(dst, '\\', 'r') + case '\t': + dst = append(dst, '\\', 't') + default: + dst = append(dst, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF]) + } + i++ + start = i + } + if start < len(s) { + dst = append(dst, s[start:]...) + } + return dst +} + +// AppendBytes is a mirror of appendString with []byte arg +func AppendBytes(dst, s []byte) []byte { + dst = append(dst, '"') + for i := 0; i < len(s); i++ { + if s[i] < 0x20 || s[i] > 0x7e || s[i] == '\\' || s[i] == '"' { + dst = appendBytesComplex(dst, s, i) + return append(dst, '"') + } + } + dst = append(dst, s...) + return append(dst, '"') +} + +// appendBytesComplex is a mirror of the appendStringComplex +// with []byte arg +func appendBytesComplex(dst, s []byte, i int) []byte { + start := 0 + for i < len(s) { + b := s[i] + if b >= utf8.RuneSelf { + r, size := utf8.DecodeRune(s[i:]) + if r == utf8.RuneError && size == 1 { + if start < i { + dst = append(dst, s[start:i]...) + } + dst = append(dst, `\ufffd`...) + i += size + start = i + continue + } + i += size + continue + } + if b >= 0x20 && b <= 0x7e && b != '\\' && b != '"' { + i++ + continue + } + // We encountered a character that needs to be encoded. + // Let's append the previous simple characters to the byte slice + // and switch our operation to read and encode the remainder + // characters byte-by-byte. + if start < i { + dst = append(dst, s[start:i]...) + } + switch b { + case '"', '\\': + dst = append(dst, '\\', b) + case '\b': + dst = append(dst, '\\', 'b') + case '\f': + dst = append(dst, '\\', 'f') + case '\n': + dst = append(dst, '\\', 'n') + case '\r': + dst = append(dst, '\\', 'r') + case '\t': + dst = append(dst, '\\', 't') + default: + dst = append(dst, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF]) + } + i++ + start = i + } + if start < len(s) { + dst = append(dst, s[start:]...) + } + return dst +} diff --git a/vendor/github.com/rs/zerolog/internal/json/time.go b/vendor/github.com/rs/zerolog/internal/json/time.go new file mode 100644 index 000000000..612438dbc --- /dev/null +++ b/vendor/github.com/rs/zerolog/internal/json/time.go @@ -0,0 +1,68 @@ +package json + +import ( + "strconv" + "time" +) + +func AppendTime(dst []byte, t time.Time, format string) []byte { + if format == "" { + return AppendInt64(dst, t.Unix()) + } + return append(t.AppendFormat(append(dst, '"'), format), '"') +} + +func AppendTimes(dst []byte, vals []time.Time, format string) []byte { + if format == "" { + return appendUnixTimes(dst, vals) + } + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = append(vals[0].AppendFormat(append(dst, '"'), format), '"') + if len(vals) > 1 { + for _, t := range vals[1:] { + dst = append(t.AppendFormat(append(dst, ',', '"'), format), '"') + } + } + dst = append(dst, ']') + return dst +} + +func appendUnixTimes(dst []byte, vals []time.Time) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendInt(dst, vals[0].Unix(), 10) + if len(vals) > 1 { + for _, t := range vals[1:] { + dst = strconv.AppendInt(dst, t.Unix(), 10) + } + } + dst = append(dst, ']') + return dst +} + +func AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool) []byte { + if useInt { + return strconv.AppendInt(dst, int64(d/unit), 10) + } + return AppendFloat64(dst, float64(d)/float64(unit)) +} + +func AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = AppendDuration(dst, vals[0], unit, useInt) + if len(vals) > 1 { + for _, d := range vals[1:] { + dst = AppendDuration(append(dst, ','), d, unit, useInt) + } + } + dst = append(dst, ']') + return dst +} diff --git a/vendor/github.com/rs/zerolog/internal/json/types.go b/vendor/github.com/rs/zerolog/internal/json/types.go new file mode 100644 index 000000000..bbc8e427a --- /dev/null +++ b/vendor/github.com/rs/zerolog/internal/json/types.go @@ -0,0 +1,278 @@ +package json + +import ( + "encoding/json" + "fmt" + "math" + "strconv" +) + +func AppendBool(dst []byte, val bool) []byte { + return strconv.AppendBool(dst, val) +} + +func AppendBools(dst []byte, vals []bool) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendBool(dst, vals[0]) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = strconv.AppendBool(append(dst, ','), val) + } + } + dst = append(dst, ']') + return dst +} + +func AppendInt(dst []byte, val int) []byte { + return strconv.AppendInt(dst, int64(val), 10) +} + +func AppendInts(dst []byte, vals []int) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendInt(dst, int64(vals[0]), 10) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = strconv.AppendInt(append(dst, ','), int64(val), 10) + } + } + dst = append(dst, ']') + return dst +} + +func AppendInt8(dst []byte, val int8) []byte { + return strconv.AppendInt(dst, int64(val), 10) +} + +func AppendInts8(dst []byte, vals []int8) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendInt(dst, int64(vals[0]), 10) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = strconv.AppendInt(append(dst, ','), int64(val), 10) + } + } + dst = append(dst, ']') + return dst +} + +func AppendInt16(dst []byte, val int16) []byte { + return strconv.AppendInt(dst, int64(val), 10) +} + +func AppendInts16(dst []byte, vals []int16) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendInt(dst, int64(vals[0]), 10) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = strconv.AppendInt(append(dst, ','), int64(val), 10) + } + } + dst = append(dst, ']') + return dst +} + +func AppendInt32(dst []byte, val int32) []byte { + return strconv.AppendInt(dst, int64(val), 10) +} + +func AppendInts32(dst []byte, vals []int32) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendInt(dst, int64(vals[0]), 10) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = strconv.AppendInt(append(dst, ','), int64(val), 10) + } + } + dst = append(dst, ']') + return dst +} + +func AppendInt64(dst []byte, val int64) []byte { + return strconv.AppendInt(dst, val, 10) +} + +func AppendInts64(dst []byte, vals []int64) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendInt(dst, vals[0], 10) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = strconv.AppendInt(append(dst, ','), val, 10) + } + } + dst = append(dst, ']') + return dst +} + +func AppendUint(dst []byte, val uint) []byte { + return strconv.AppendUint(dst, uint64(val), 10) +} + +func AppendUints(dst []byte, vals []uint) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendUint(dst, uint64(vals[0]), 10) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = strconv.AppendUint(append(dst, ','), uint64(val), 10) + } + } + dst = append(dst, ']') + return dst +} + +func AppendUint8(dst []byte, val uint8) []byte { + return strconv.AppendUint(dst, uint64(val), 10) +} + +func AppendUints8(dst []byte, vals []uint8) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendUint(dst, uint64(vals[0]), 10) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = strconv.AppendUint(append(dst, ','), uint64(val), 10) + } + } + dst = append(dst, ']') + return dst +} + +func AppendUint16(dst []byte, val uint16) []byte { + return strconv.AppendUint(dst, uint64(val), 10) +} + +func AppendUints16(dst []byte, vals []uint16) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendUint(dst, uint64(vals[0]), 10) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = strconv.AppendUint(append(dst, ','), uint64(val), 10) + } + } + dst = append(dst, ']') + return dst +} + +func AppendUint32(dst []byte, val uint32) []byte { + return strconv.AppendUint(dst, uint64(val), 10) +} + +func AppendUints32(dst []byte, vals []uint32) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendUint(dst, uint64(vals[0]), 10) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = strconv.AppendUint(append(dst, ','), uint64(val), 10) + } + } + dst = append(dst, ']') + return dst +} + +func AppendUint64(dst []byte, val uint64) []byte { + return strconv.AppendUint(dst, uint64(val), 10) +} + +func AppendUints64(dst []byte, vals []uint64) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = strconv.AppendUint(dst, vals[0], 10) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = strconv.AppendUint(append(dst, ','), val, 10) + } + } + dst = append(dst, ']') + return dst +} + +func AppendFloat(dst []byte, val float64, bitSize int) []byte { + // JSON does not permit NaN or Infinity. A typical JSON encoder would fail + // with an error, but a logging library wants the data to get thru so we + // make a tradeoff and store those types as string. + switch { + case math.IsNaN(val): + return append(dst, `"NaN"`...) + case math.IsInf(val, 1): + return append(dst, `"+Inf"`...) + case math.IsInf(val, -1): + return append(dst, `"-Inf"`...) + } + return strconv.AppendFloat(dst, val, 'f', -1, bitSize) +} + +func AppendFloat32(dst []byte, val float32) []byte { + return AppendFloat(dst, float64(val), 32) +} + +func AppendFloats32(dst []byte, vals []float32) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = AppendFloat(dst, float64(vals[0]), 32) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = AppendFloat(append(dst, ','), float64(val), 32) + } + } + dst = append(dst, ']') + return dst +} + +func AppendFloat64(dst []byte, val float64) []byte { + return AppendFloat(dst, val, 64) +} + +func AppendFloats64(dst []byte, vals []float64) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = AppendFloat(dst, vals[0], 32) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = AppendFloat(append(dst, ','), val, 64) + } + } + dst = append(dst, ']') + return dst +} + +func AppendInterface(dst []byte, i interface{}) []byte { + marshaled, err := json.Marshal(i) + if err != nil { + return AppendString(dst, fmt.Sprintf("marshaling error: %v", err)) + } + return append(dst, marshaled...) +} diff --git a/vendor/github.com/rs/zerolog/log.go b/vendor/github.com/rs/zerolog/log.go new file mode 100644 index 000000000..71db0b505 --- /dev/null +++ b/vendor/github.com/rs/zerolog/log.go @@ -0,0 +1,338 @@ +// Package zerolog provides a lightweight logging library dedicated to JSON logging. +// +// A global Logger can be use for simple logging: +// +// import "github.com/rs/zerolog/log" +// +// log.Info().Msg("hello world") +// // Output: {"time":1494567715,"level":"info","message":"hello world"} +// +// NOTE: To import the global logger, import the "log" subpackage "github.com/rs/zerolog/log". +// +// Fields can be added to log messages: +// +// log.Info().Str("foo", "bar").Msg("hello world") +// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"} +// +// Create logger instance to manage different outputs: +// +// logger := zerolog.New(os.Stderr).With().Timestamp().Logger() +// logger.Info(). +// Str("foo", "bar"). +// Msg("hello world") +// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"} +// +// Sub-loggers let you chain loggers with additional context: +// +// sublogger := log.With().Str("component": "foo").Logger() +// sublogger.Info().Msg("hello world") +// // Output: {"time":1494567715,"level":"info","message":"hello world","component":"foo"} +// +// Level logging +// +// zerolog.SetGlobalLevel(zerolog.InfoLevel) +// +// log.Debug().Msg("filtered out message") +// log.Info().Msg("routed message") +// +// if e := log.Debug(); e.Enabled() { +// // Compute log output only if enabled. +// value := compute() +// e.Str("foo": value).Msg("some debug message") +// } +// // Output: {"level":"info","time":1494567715,"routed message"} +// +// Customize automatic field names: +// +// log.TimestampFieldName = "t" +// log.LevelFieldName = "p" +// log.MessageFieldName = "m" +// +// log.Info().Msg("hello world") +// // Output: {"t":1494567715,"p":"info","m":"hello world"} +// +// Log with no level and message: +// +// log.Log().Str("foo","bar").Msg("") +// // Output: {"time":1494567715,"foo":"bar"} +// +// Add contextual fields to global Logger: +// +// log.Logger = log.With().Str("foo", "bar").Logger() +// +// Sample logs: +// +// sampled := log.Sample(10) +// sampled.Info().Msg("will be logged every 10 messages") +// +package zerolog + +import ( + "io" + "io/ioutil" + "os" + "strconv" + "sync/atomic" + + "github.com/rs/zerolog/internal/json" +) + +// Level defines log levels. +type Level uint8 + +const ( + // DebugLevel defines debug log level. + DebugLevel Level = iota + // InfoLevel defines info log level. + InfoLevel + // WarnLevel defines warn log level. + WarnLevel + // ErrorLevel defines error log level. + ErrorLevel + // FatalLevel defines fatal log level. + FatalLevel + // PanicLevel defines panic log level. + PanicLevel + // Disabled disables the logger. + Disabled +) + +func (l Level) String() string { + switch l { + case DebugLevel: + return "debug" + case InfoLevel: + return "info" + case WarnLevel: + return "warn" + case ErrorLevel: + return "error" + case FatalLevel: + return "fatal" + case PanicLevel: + return "panic" + } + return "" +} + +const ( + // Often samples log every 10 events. + Often = 10 + // Sometimes samples log every 100 events. + Sometimes = 100 + // Rarely samples log every 1000 events. + Rarely = 1000 +) + +var disabledEvent = newEvent(levelWriterAdapter{ioutil.Discard}, 0, false) + +// A Logger represents an active logging object that generates lines +// of JSON output to an io.Writer. Each logging operation makes a single +// call to the Writer's Write method. There is no guaranty on access +// serialization to the Writer. If your Writer is not thread safe, +// you may consider a sync wrapper. +type Logger struct { + w LevelWriter + level Level + sample uint32 + counter *uint32 + context []byte +} + +// New creates a root logger with given output writer. If the output writer implements +// the LevelWriter interface, the WriteLevel method will be called instead of the Write +// one. +// +// Each logging operation makes a single call to the Writer's Write method. There is no +// guaranty on access serialization to the Writer. If your Writer is not thread safe, +// you may consider using sync wrapper. +func New(w io.Writer) Logger { + if w == nil { + w = ioutil.Discard + } + lw, ok := w.(LevelWriter) + if !ok { + lw = levelWriterAdapter{w} + } + return Logger{w: lw} +} + +// Nop returns a disabled logger for which all operation are no-op. +func Nop() Logger { + return New(nil).Level(Disabled) +} + +// With creates a child logger with the field added to its context. +func (l Logger) With() Context { + context := l.context + l.context = make([]byte, 0, 500) + if context != nil { + l.context = append(l.context, context...) + } else { + // first byte of context is presence of timestamp or not + l.context = append(l.context, 0) + } + return Context{l} +} + +// Level creates a child logger with the minimum accepted level set to level. +func (l Logger) Level(lvl Level) Logger { + return Logger{ + w: l.w, + level: lvl, + sample: l.sample, + counter: l.counter, + context: l.context, + } +} + +// Sample returns a logger that only let one message out of every to pass thru. +func (l Logger) Sample(every int) Logger { + if every == 0 { + // Create a child with no sampling. + return Logger{ + w: l.w, + level: l.level, + context: l.context, + } + } + return Logger{ + w: l.w, + level: l.level, + sample: uint32(every), + counter: new(uint32), + context: l.context, + } +} + +// Debug starts a new message with debug level. +// +// You must call Msg on the returned event in order to send the event. +func (l Logger) Debug() *Event { + return l.newEvent(DebugLevel, true, nil) +} + +// Info starts a new message with info level. +// +// You must call Msg on the returned event in order to send the event. +func (l Logger) Info() *Event { + return l.newEvent(InfoLevel, true, nil) +} + +// Warn starts a new message with warn level. +// +// You must call Msg on the returned event in order to send the event. +func (l Logger) Warn() *Event { + return l.newEvent(WarnLevel, true, nil) +} + +// Error starts a new message with error level. +// +// You must call Msg on the returned event in order to send the event. +func (l Logger) Error() *Event { + return l.newEvent(ErrorLevel, true, nil) +} + +// Fatal starts a new message with fatal level. The os.Exit(1) function +// is called by the Msg method. +// +// You must call Msg on the returned event in order to send the event. +func (l Logger) Fatal() *Event { + return l.newEvent(FatalLevel, true, func(msg string) { os.Exit(1) }) +} + +// Panic starts a new message with panic level. The message is also sent +// to the panic function. +// +// You must call Msg on the returned event in order to send the event. +func (l Logger) Panic() *Event { + return l.newEvent(PanicLevel, true, func(msg string) { panic(msg) }) +} + +// WithLevel starts a new message with level. +// +// You must call Msg on the returned event in order to send the event. +func (l Logger) WithLevel(level Level) *Event { + switch level { + case DebugLevel: + return l.Debug() + case InfoLevel: + return l.Info() + case WarnLevel: + return l.Warn() + case ErrorLevel: + return l.Error() + case FatalLevel: + return l.Fatal() + case PanicLevel: + return l.Panic() + case Disabled: + return disabledEvent + default: + panic("zerolog: WithLevel(): invalid level: " + strconv.Itoa(int(level))) + } +} + +// Log starts a new message with no level. Setting GlobalLevel to Disabled +// will still disable events produced by this method. +// +// You must call Msg on the returned event in order to send the event. +func (l Logger) Log() *Event { + // We use panic level with addLevelField=false to make Log passthrough all + // levels except Disabled. + return l.newEvent(PanicLevel, false, nil) +} + +// Write implements the io.Writer interface. This is useful to set as a writer +// for the standard library log. +func (l Logger) Write(p []byte) (n int, err error) { + n = len(p) + if n > 0 && p[n-1] == '\n' { + // Trim CR added by stdlog. + p = p[0 : n-1] + } + l.Log().Msg(string(p)) + return +} + +func (l Logger) newEvent(level Level, addLevelField bool, done func(string)) *Event { + enabled := l.should(level) + if !enabled { + return disabledEvent + } + lvl := InfoLevel + if addLevelField { + lvl = level + } + e := newEvent(l.w, lvl, enabled) + e.done = done + if l.context != nil && len(l.context) > 0 && l.context[0] > 0 { + // first byte of context is ts flag + e.buf = json.AppendTime(json.AppendKey(e.buf, TimestampFieldName), TimestampFunc(), TimeFieldFormat) + } + if addLevelField { + e.Str(LevelFieldName, level.String()) + } + if l.sample > 0 && SampleFieldName != "" { + e.Uint32(SampleFieldName, l.sample) + } + if l.context != nil && len(l.context) > 1 { + if len(e.buf) > 1 { + e.buf = append(e.buf, ',') + } + e.buf = append(e.buf, l.context[1:]...) + } + return e +} + +// should returns true if the log event should be logged. +func (l Logger) should(lvl Level) bool { + if lvl < l.level || lvl < globalLevel() { + return false + } + if l.sample > 0 && l.counter != nil && !samplingDisabled() { + c := atomic.AddUint32(l.counter, 1) + return c%l.sample == 0 + } + return true +} diff --git a/vendor/github.com/rs/zerolog/log/log.go b/vendor/github.com/rs/zerolog/log/log.go new file mode 100644 index 000000000..a4785967d --- /dev/null +++ b/vendor/github.com/rs/zerolog/log/log.go @@ -0,0 +1,85 @@ +// Package log provides a global logger for zerolog. +package log + +import ( + "context" + "os" + + "github.com/rs/zerolog" +) + +// Logger is the global logger. +var Logger = zerolog.New(os.Stderr).With().Timestamp().Logger() + +// With creates a child logger with the field added to its context. +func With() zerolog.Context { + return Logger.With() +} + +// Level crestes a child logger with the minium accepted level set to level. +func Level(level zerolog.Level) zerolog.Logger { + return Logger.Level(level) +} + +// Sample returns a logger that only let one message out of every to pass thru. +func Sample(every int) zerolog.Logger { + return Logger.Sample(every) +} + +// Debug starts a new message with debug level. +// +// You must call Msg on the returned event in order to send the event. +func Debug() *zerolog.Event { + return Logger.Debug() +} + +// Info starts a new message with info level. +// +// You must call Msg on the returned event in order to send the event. +func Info() *zerolog.Event { + return Logger.Info() +} + +// Warn starts a new message with warn level. +// +// You must call Msg on the returned event in order to send the event. +func Warn() *zerolog.Event { + return Logger.Warn() +} + +// Error starts a new message with error level. +// +// You must call Msg on the returned event in order to send the event. +func Error() *zerolog.Event { + return Logger.Error() +} + +// Fatal starts a new message with fatal level. The os.Exit(1) function +// is called by the Msg method. +// +// You must call Msg on the returned event in order to send the event. +func Fatal() *zerolog.Event { + return Logger.Fatal() +} + +// Panic starts a new message with panic level. The message is also sent +// to the panic function. +// +// You must call Msg on the returned event in order to send the event. +func Panic() *zerolog.Event { + return Logger.Panic() +} + +// Log starts a new message with no level. Setting zerolog.GlobalLevel to +// zerlog.Disabled will still disable events produced by this method. +// +// You must call Msg on the returned event in order to send the event. +func Log() *zerolog.Event { + return Logger.Log() +} + +// Ctx returns the Logger associated with the ctx. If no logger +// is associated, a disabled logger is returned. +func Ctx(ctx context.Context) zerolog.Logger { + return zerolog.Ctx(ctx) +} diff --git a/vendor/github.com/rs/zerolog/syslog.go b/vendor/github.com/rs/zerolog/syslog.go new file mode 100644 index 000000000..8d56ba9b2 --- /dev/null +++ b/vendor/github.com/rs/zerolog/syslog.go @@ -0,0 +1,52 @@ +// +build !windows + +package zerolog + +import "io" + +// SyslogWriter is an interface matching a syslog.Writer struct. +type SyslogWriter interface { + io.Writer + Debug(m string) error + Info(m string) error + Warning(m string) error + Err(m string) error + Emerg(m string) error + Crit(m string) error +} + +type syslogWriter struct { + w SyslogWriter +} + +// SyslogLevelWriter wraps a SyslogWriter and call the right syslog level +// method matching the zerolog level. +func SyslogLevelWriter(w SyslogWriter) LevelWriter { + return syslogWriter{w} +} + +func (sw syslogWriter) Write(p []byte) (n int, err error) { + return sw.w.Write(p) +} + +// WriteLevel implements LevelWriter interface. +func (sw syslogWriter) WriteLevel(level Level, p []byte) (n int, err error) { + switch level { + case DebugLevel: + err = sw.w.Debug(string(p)) + case InfoLevel: + err = sw.w.Info(string(p)) + case WarnLevel: + err = sw.w.Warning(string(p)) + case ErrorLevel: + err = sw.w.Err(string(p)) + case FatalLevel: + err = sw.w.Emerg(string(p)) + case PanicLevel: + err = sw.w.Crit(string(p)) + default: + panic("invalid level") + } + n = len(p) + return +} diff --git a/vendor/github.com/rs/zerolog/writer.go b/vendor/github.com/rs/zerolog/writer.go new file mode 100644 index 000000000..a58d71776 --- /dev/null +++ b/vendor/github.com/rs/zerolog/writer.go @@ -0,0 +1,100 @@ +package zerolog + +import ( + "io" + "sync" +) + +// LevelWriter defines as interface a writer may implement in order +// to receive level information with payload. +type LevelWriter interface { + io.Writer + WriteLevel(level Level, p []byte) (n int, err error) +} + +type levelWriterAdapter struct { + io.Writer +} + +func (lw levelWriterAdapter) WriteLevel(l Level, p []byte) (n int, err error) { + return lw.Write(p) +} + +type syncWriter struct { + mu sync.Mutex + lw LevelWriter +} + +// SyncWriter wraps w so that each call to Write is synchronized with a mutex. +// This syncer can be the call to writer's Write method is not thread safe. +// Note that os.File Write operation is using write() syscall which is supposed +// to be thread-safe on POSIX systems. So there is no need to use this with +// os.File on such systems as zerolog guaranties to issue a single Write call +// per log event. +func SyncWriter(w io.Writer) io.Writer { + if lw, ok := w.(LevelWriter); ok { + return &syncWriter{lw: lw} + } + return &syncWriter{lw: levelWriterAdapter{w}} +} + +// Write implements the io.Writer interface. +func (s *syncWriter) Write(p []byte) (n int, err error) { + s.mu.Lock() + defer s.mu.Unlock() + return s.lw.Write(p) +} + +// WriteLevel implements the LevelWriter interface. +func (s *syncWriter) WriteLevel(l Level, p []byte) (n int, err error) { + s.mu.Lock() + defer s.mu.Unlock() + return s.lw.WriteLevel(l, p) +} + +type multiLevelWriter struct { + writers []LevelWriter +} + +func (t multiLevelWriter) Write(p []byte) (n int, err error) { + for _, w := range t.writers { + n, err = w.Write(p) + if err != nil { + return + } + if n != len(p) { + err = io.ErrShortWrite + return + } + } + return len(p), nil +} + +func (t multiLevelWriter) WriteLevel(l Level, p []byte) (n int, err error) { + for _, w := range t.writers { + n, err = w.WriteLevel(l, p) + if err != nil { + return + } + if n != len(p) { + err = io.ErrShortWrite + return + } + } + return len(p), nil +} + +// MultiLevelWriter creates a writer that duplicates its writes to all the +// provided writers, similar to the Unix tee(1) command. If some writers +// implement LevelWriter, their WriteLevel method will be used instead of Write. +func MultiLevelWriter(writers ...io.Writer) LevelWriter { + lwriters := make([]LevelWriter, 0, len(writers)) + for _, w := range writers { + if lw, ok := w.(LevelWriter); ok { + lwriters = append(lwriters, lw) + } else { + lwriters = append(lwriters, levelWriterAdapter{w}) + } + } + return multiLevelWriter{lwriters} +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 74bf05a4a..335f477d8 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -748,6 +748,24 @@ "revision": "6ac8c5d890d415025dd5aae7595bcb2a6e7e2fad", "revisionTime": "2017-04-24T20:45:52Z" }, + { + "checksumSHA1": "fGBeb3o1grSXGUNAjwptkBWfch0=", + "path": "github.com/rs/zerolog", + "revision": "89ff8dbc5f047ae9957523b07e627891079f7967", + "revisionTime": "2017-07-27T06:42:12Z" + }, + { + "checksumSHA1": "AREhk6LKIp2I/4Njd756bqU6JSQ=", + "path": "github.com/rs/zerolog/internal/json", + "revision": "89ff8dbc5f047ae9957523b07e627891079f7967", + "revisionTime": "2017-07-27T06:42:12Z" + }, + { + "checksumSHA1": "kolarHDX6fkauW+1KWx1SFqSF2o=", + "path": "github.com/rs/zerolog/log", + "revision": "89ff8dbc5f047ae9957523b07e627891079f7967", + "revisionTime": "2017-07-27T06:42:12Z" + }, { "path": "github.com/russross/meddler", "revision": "308c3d0e5e45f543a2eb6c787cbfe0db3880e220", diff --git a/version/version.go b/version/version.go index d836006f5..bedfec3ac 100644 --- a/version/version.go +++ b/version/version.go @@ -10,7 +10,7 @@ var ( // VersionPatch is for backwards-compatible bug fixes VersionPatch int64 = 0 // VersionPre indicates prerelease - VersionPre string = "rc.3" + VersionPre string // VersionDev indicates development branch. Releases will be empty string. VersionDev string )