mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-23 08:56:29 +00:00
Merge pull request #2209 from bradrydzewski/master
Provide a basic agent healthcheck
This commit is contained in:
commit
f5fc076bc1
15 changed files with 632 additions and 80 deletions
|
@ -1,7 +1,11 @@
|
|||
# docker build --rm -t drone/drone .
|
||||
# docker build --rm -f Dockerfile.agent -t drone/agent .
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
FROM centurylink/ca-certs
|
||||
ENV GODEBUG=netdns=go
|
||||
ADD release/drone-agent /bin/
|
||||
|
||||
HEALTHCHECK CMD ["/bin/drone-agent", "ping"]
|
||||
|
||||
ENTRYPOINT ["/bin/drone-agent"]
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
# docker build --rm -t drone/drone .
|
||||
|
||||
FROM centurylink/ca-certs
|
||||
ENV GODEBUG=netdns=go
|
||||
ENV DRONE_PLATFORM=linux/arm
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
# docker build --rm -t drone/drone .
|
||||
|
||||
FROM centurylink/ca-certs
|
||||
ENV GODEBUG=netdns=go
|
||||
ENV DRONE_PLATFORM=linux/arm64
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
@ -46,6 +47,22 @@ func loop(c *cli.Context) error {
|
|||
zerolog.SetGlobalLevel(zerolog.WarnLevel)
|
||||
}
|
||||
|
||||
if c.Bool("pretty") {
|
||||
log.Logger = log.Output(
|
||||
zerolog.ConsoleWriter{
|
||||
Out: os.Stderr,
|
||||
NoColor: c.BoolT("nocolor"),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
counter.Polling = c.Int("max-procs")
|
||||
counter.Running = 0
|
||||
|
||||
if c.BoolT("healthcheck") {
|
||||
go http.ListenAndServe(":3000", nil)
|
||||
}
|
||||
|
||||
// TODO pass version information to grpc server
|
||||
// TODO authenticate to grpc server
|
||||
|
||||
|
@ -124,9 +141,22 @@ func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
timeout := time.Hour
|
||||
if minutes := work.Timeout; minutes != 0 {
|
||||
timeout = time.Duration(minutes) * time.Minute
|
||||
}
|
||||
|
||||
counter.Add(
|
||||
work.ID,
|
||||
timeout,
|
||||
extractRepositoryName(work.Config), // hack
|
||||
extractBuildNumber(work.Config), // hack
|
||||
)
|
||||
defer counter.Done(work.ID)
|
||||
|
||||
logger := log.With().
|
||||
Str("repo", extractRepositoryName(work.Config)).
|
||||
Str("build", extractBuildNumber(work.Config)).
|
||||
Str("repo", extractRepositoryName(work.Config)). // hack
|
||||
Str("build", extractBuildNumber(work.Config)). // hack
|
||||
Str("id", work.ID).
|
||||
Logger()
|
||||
|
||||
|
@ -143,11 +173,6 @@ func run(ctx context.Context, client rpc.Peer, filter rpc.Filter) error {
|
|||
return err
|
||||
}
|
||||
|
||||
timeout := time.Hour
|
||||
if minutes := work.Timeout; minutes != 0 {
|
||||
timeout = time.Duration(minutes) * time.Minute
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctxmeta, timeout)
|
||||
defer cancel()
|
||||
|
||||
|
|
131
cmd/drone-agent/health.go
Normal file
131
cmd/drone-agent/health.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/version"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// the file implements some basic healthcheck logic based on the
|
||||
// following specification:
|
||||
// https://github.com/mozilla-services/Dockerflow
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/varz", handleStats)
|
||||
http.HandleFunc("/healthz", handleHeartbeat)
|
||||
http.HandleFunc("/version", handleVersion)
|
||||
}
|
||||
|
||||
func handleHeartbeat(w http.ResponseWriter, r *http.Request) {
|
||||
if counter.Healthy() {
|
||||
w.WriteHeader(200)
|
||||
} else {
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
}
|
||||
|
||||
func handleVersion(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Header().Add("Content-Type", "text/json")
|
||||
json.NewEncoder(w).Encode(versionResp{
|
||||
Source: "https://github.com/drone/drone",
|
||||
Version: version.Version.String(),
|
||||
})
|
||||
}
|
||||
|
||||
func handleStats(w http.ResponseWriter, r *http.Request) {
|
||||
if counter.Healthy() {
|
||||
w.WriteHeader(200)
|
||||
} else {
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
w.Header().Add("Content-Type", "text/json")
|
||||
counter.writeTo(w)
|
||||
}
|
||||
|
||||
type versionResp struct {
|
||||
Version string `json:"version"`
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
// default statistics counter
|
||||
var counter = &state{
|
||||
Metadata: map[string]info{},
|
||||
}
|
||||
|
||||
type state struct {
|
||||
sync.Mutex `json:"-"`
|
||||
Polling int `json:"polling_count"`
|
||||
Running int `json:"running_count"`
|
||||
Metadata map[string]info `json:"running"`
|
||||
}
|
||||
|
||||
type info struct {
|
||||
ID string `json:"id"`
|
||||
Repo string `json:"repository"`
|
||||
Build string `json:"build_number"`
|
||||
Started time.Time `json:"build_started"`
|
||||
Timeout time.Duration `json:"build_timeout"`
|
||||
}
|
||||
|
||||
func (s *state) Add(id string, timeout time.Duration, repo, build string) {
|
||||
s.Lock()
|
||||
s.Polling--
|
||||
s.Running++
|
||||
s.Metadata[id] = info{
|
||||
ID: id,
|
||||
Repo: repo,
|
||||
Build: build,
|
||||
Timeout: timeout,
|
||||
Started: time.Now().UTC(),
|
||||
}
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func (s *state) Done(id string) {
|
||||
s.Lock()
|
||||
s.Polling++
|
||||
s.Running--
|
||||
delete(s.Metadata, id)
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func (s *state) Healthy() bool {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
now := time.Now()
|
||||
buf := time.Hour // 1 hour buffer
|
||||
for _, item := range s.Metadata {
|
||||
if now.After(item.Started.Add(item.Timeout).Add(buf)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *state) writeTo(w io.Writer) (int, error) {
|
||||
s.Lock()
|
||||
out, _ := json.Marshal(s)
|
||||
s.Unlock()
|
||||
return w.Write(out)
|
||||
}
|
||||
|
||||
// handles pinging the endpoint and returns an error if the
|
||||
// agent is in an unhealthy state.
|
||||
func pinger(c *cli.Context) error {
|
||||
resp, err := http.Get("http://localhost:3000/healthz")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("agent returned non-200 status code")
|
||||
}
|
||||
return nil
|
||||
}
|
45
cmd/drone-agent/health_test.go
Normal file
45
cmd/drone-agent/health_test.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestHealthy(t *testing.T) {
|
||||
s := state{}
|
||||
s.Metadata = map[string]info{}
|
||||
|
||||
s.Add("1", time.Hour, "octocat/hello-world", "42")
|
||||
|
||||
if got, want := s.Metadata["1"].ID, "1"; got != want {
|
||||
t.Errorf("got ID %s, want %s", got, want)
|
||||
}
|
||||
if got, want := s.Metadata["1"].Timeout, time.Hour; got != want {
|
||||
t.Errorf("got duration %v, want %v", got, want)
|
||||
}
|
||||
if got, want := s.Metadata["1"].Repo, "octocat/hello-world"; got != want {
|
||||
t.Errorf("got repository name %s, want %s", got, want)
|
||||
}
|
||||
|
||||
s.Metadata["1"] = info{
|
||||
Timeout: time.Hour,
|
||||
Started: time.Now().UTC(),
|
||||
}
|
||||
if s.Healthy() == false {
|
||||
t.Error("want healthy status when timeout not exceeded, got false")
|
||||
}
|
||||
|
||||
s.Metadata["1"] = info{
|
||||
Started: time.Now().UTC().Add(-(time.Minute * 30)),
|
||||
}
|
||||
if s.Healthy() == false {
|
||||
t.Error("want healthy status when timeout+buffer not exceeded, got false")
|
||||
}
|
||||
|
||||
s.Metadata["1"] = info{
|
||||
Started: time.Now().UTC().Add(-(time.Hour + time.Minute)),
|
||||
}
|
||||
if s.Healthy() == true {
|
||||
t.Error("want unhealthy status when timeout+buffer not exceeded, got true")
|
||||
}
|
||||
}
|
|
@ -16,6 +16,13 @@ func main() {
|
|||
app.Version = version.Version.String()
|
||||
app.Usage = "drone agent"
|
||||
app.Action = loop
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "ping",
|
||||
Usage: "ping the agent",
|
||||
Action: pinger,
|
||||
},
|
||||
}
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_SERVER",
|
||||
|
@ -39,6 +46,16 @@ func main() {
|
|||
Name: "debug",
|
||||
Usage: "start the agent in debug mode",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_DEBUG_PRETTY",
|
||||
Name: "pretty",
|
||||
Usage: "enable pretty-printed debug output",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
EnvVar: "DRONE_DEBUG_NOCOLOR",
|
||||
Name: "nocolor",
|
||||
Usage: "disable colored debug output",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_HOSTNAME,HOSTNAME",
|
||||
Name: "hostname",
|
||||
|
@ -50,14 +67,19 @@ func main() {
|
|||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_FILTER",
|
||||
Name: "drone-filter",
|
||||
Usage: "A filter expression used to restrict builds by label",
|
||||
Name: "filter",
|
||||
Usage: "filter expression used to restrict builds by label",
|
||||
},
|
||||
cli.IntFlag{
|
||||
EnvVar: "DRONE_MAX_PROCS",
|
||||
Name: "max-procs",
|
||||
Value: 1,
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
EnvVar: "DRONE_HEALTHCHECK",
|
||||
Name: "healthcheck",
|
||||
Usage: "enables the healthcheck endpoint",
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
|
|
58
vendor/github.com/rs/zerolog/README.md
generated
vendored
58
vendor/github.com/rs/zerolog/README.md
generated
vendored
|
@ -8,16 +8,20 @@ Zerolog's API is designed to provide both a great developer experience and stunn
|
|||
|
||||
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.
|
||||
To keep the code base and the API simple, zerolog focuses on JSON logging only. Pretty logging on the console is made possible using the provided (but inefficient) `zerolog.ConsoleWriter`.
|
||||
|
||||
![](pretty.png)
|
||||
|
||||
## Features
|
||||
|
||||
* Blazing fast
|
||||
* Low to zero allocation
|
||||
* Level logging
|
||||
* Sampling
|
||||
* Contextual fields
|
||||
* `context.Context` integration
|
||||
* `net/http` helpers
|
||||
* Pretty logging for development
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -27,6 +31,13 @@ import "github.com/rs/zerolog/log"
|
|||
|
||||
### A global logger can be use for simple logging
|
||||
|
||||
```go
|
||||
log.Print("hello world")
|
||||
|
||||
// Output: {"level":"debug","time":1494567715,"message":"hello world"}
|
||||
```
|
||||
|
||||
|
||||
```go
|
||||
log.Info().Msg("hello world")
|
||||
|
||||
|
@ -96,6 +107,18 @@ if e := log.Debug(); e.Enabled() {
|
|||
// Output: {"level":"info","time":1494567715,"message":"routed message"}
|
||||
```
|
||||
|
||||
### Pretty logging
|
||||
|
||||
```go
|
||||
if isConsole {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||
}
|
||||
|
||||
log.Info().Str("foo", "bar").Msg("Hello world")
|
||||
|
||||
// Output: 1494567715 |INFO| Hello world foo=bar
|
||||
```
|
||||
|
||||
### Sub dictionary
|
||||
|
||||
```go
|
||||
|
@ -138,12 +161,31 @@ log.Logger = log.With().Str("foo", "bar").Logger()
|
|||
### Log Sampling
|
||||
|
||||
```go
|
||||
sampled := log.Sample(10)
|
||||
sampled := log.Sample(&zerolog.BasicSampler{N: 10})
|
||||
sampled.Info().Msg("will be logged every 10 messages")
|
||||
|
||||
// Output: {"time":1494567715,"sample":10,"message":"will be logged every 10 messages"}
|
||||
// Output: {"time":1494567715,"level":"info","message":"will be logged every 10 messages"}
|
||||
```
|
||||
|
||||
More advanced sampling:
|
||||
|
||||
```go
|
||||
// Will let 5 debug messages per period of 1 second.
|
||||
// Over 5 debug message, 1 every 100 debug messages are logged.
|
||||
// Other levels are not sampled.
|
||||
sampled := log.Sample(zerolog.LevelSampler{
|
||||
DebugSampler: &zerolog.BurstSampler{
|
||||
Burst: 5,
|
||||
Period: 1*time.Second,
|
||||
NextSampler: &zerolog.BasicSampler{N: 100},
|
||||
},
|
||||
})
|
||||
sampled.Debug().Msg("hello world")
|
||||
|
||||
// Output: {"time":1494567715,"level":"debug","message":"hello world"}
|
||||
```
|
||||
|
||||
|
||||
### Pass a sub-logger by context
|
||||
|
||||
```go
|
||||
|
@ -233,7 +275,6 @@ Some settings can be changed and will by applied to all loggers:
|
|||
* `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.
|
||||
|
@ -259,7 +300,7 @@ Some settings can be changed and will by applied to all loggers:
|
|||
* `Dict`: Adds a sub-key/value as a field of the event.
|
||||
* `Interface`: Uses reflection to marshal the type.
|
||||
|
||||
## Performance
|
||||
## Benchmarks
|
||||
|
||||
All operations are allocation free (those numbers *include* JSON encoding):
|
||||
|
||||
|
@ -271,7 +312,12 @@ 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):
|
||||
There are a few Go logging benchmarks and comparisons that include zerolog.
|
||||
|
||||
- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench)
|
||||
- [uber-common/zap](https://github.com/uber-go/zap#performance)
|
||||
|
||||
Using Uber's zap comparison benchmark:
|
||||
|
||||
Log a message and 10 fields:
|
||||
|
||||
|
|
117
vendor/github.com/rs/zerolog/console.go
generated
vendored
Normal file
117
vendor/github.com/rs/zerolog/console.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
package zerolog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
cReset = 0
|
||||
cBold = 1
|
||||
cRed = 31
|
||||
cGreen = 32
|
||||
cYellow = 33
|
||||
cBlue = 34
|
||||
cMagenta = 35
|
||||
cCyan = 36
|
||||
cGray = 37
|
||||
cDarkGray = 90
|
||||
)
|
||||
|
||||
var consoleBufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return bytes.NewBuffer(make([]byte, 0, 100))
|
||||
},
|
||||
}
|
||||
|
||||
// ConsoleWriter reads a JSON object per write operation and output an
|
||||
// optionally colored human readable version on the Out writer.
|
||||
type ConsoleWriter struct {
|
||||
Out io.Writer
|
||||
NoColor bool
|
||||
}
|
||||
|
||||
func (w ConsoleWriter) Write(p []byte) (n int, err error) {
|
||||
var event map[string]interface{}
|
||||
err = json.Unmarshal(p, &event)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf := consoleBufPool.Get().(*bytes.Buffer)
|
||||
defer consoleBufPool.Put(buf)
|
||||
lvlColor := cReset
|
||||
level := "????"
|
||||
if l, ok := event[LevelFieldName].(string); ok {
|
||||
if !w.NoColor {
|
||||
lvlColor = levelColor(l)
|
||||
}
|
||||
level = strings.ToUpper(l)[0:4]
|
||||
}
|
||||
fmt.Fprintf(buf, "%s |%s| %s",
|
||||
colorize(event[TimestampFieldName], cDarkGray, !w.NoColor),
|
||||
colorize(level, lvlColor, !w.NoColor),
|
||||
colorize(event[MessageFieldName], cReset, !w.NoColor))
|
||||
fields := make([]string, 0, len(event))
|
||||
for field := range event {
|
||||
switch field {
|
||||
case LevelFieldName, TimestampFieldName, MessageFieldName:
|
||||
continue
|
||||
}
|
||||
fields = append(fields, field)
|
||||
}
|
||||
sort.Strings(fields)
|
||||
for _, field := range fields {
|
||||
fmt.Fprintf(buf, " %s=", colorize(field, cCyan, !w.NoColor))
|
||||
switch value := event[field].(type) {
|
||||
case string:
|
||||
if needsQuote(value) {
|
||||
buf.WriteString(strconv.Quote(value))
|
||||
} else {
|
||||
buf.WriteString(value)
|
||||
}
|
||||
default:
|
||||
fmt.Fprint(buf, value)
|
||||
}
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
buf.WriteTo(w.Out)
|
||||
n = len(p)
|
||||
return
|
||||
}
|
||||
|
||||
func colorize(s interface{}, color int, enabled bool) string {
|
||||
if !enabled {
|
||||
return fmt.Sprintf("%v", s)
|
||||
}
|
||||
return fmt.Sprintf("\x1b[%dm%v\x1b[0m", color, s)
|
||||
}
|
||||
|
||||
func levelColor(level string) int {
|
||||
switch level {
|
||||
case "debug":
|
||||
return cMagenta
|
||||
case "info":
|
||||
return cGreen
|
||||
case "warn":
|
||||
return cYellow
|
||||
case "error", "fatal", "panic":
|
||||
return cRed
|
||||
default:
|
||||
return cReset
|
||||
}
|
||||
}
|
||||
|
||||
func needsQuote(s string) bool {
|
||||
for i := range s {
|
||||
if s[i] < 0x20 || s[i] > 0x7e || s[i] == ' ' || s[i] == '\\' || s[i] == '"' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
25
vendor/github.com/rs/zerolog/ctx.go
generated
vendored
25
vendor/github.com/rs/zerolog/ctx.go
generated
vendored
|
@ -5,25 +5,42 @@ import (
|
|||
"io/ioutil"
|
||||
)
|
||||
|
||||
var disabledLogger = New(ioutil.Discard).Level(Disabled)
|
||||
var disabledLogger *Logger
|
||||
|
||||
func init() {
|
||||
l := New(ioutil.Discard).Level(Disabled)
|
||||
disabledLogger = &l
|
||||
}
|
||||
|
||||
type ctxKey struct{}
|
||||
|
||||
// WithContext returns a copy of ctx with l associated.
|
||||
// WithContext returns a copy of ctx with l associated. If an instance of Logger
|
||||
// is already in the context, the pointer to this logger is updated with l.
|
||||
//
|
||||
// For instance, to add a field to an existing logger in the context, use this
|
||||
// notation:
|
||||
//
|
||||
// ctx := r.Context()
|
||||
// l := zerolog.Ctx(ctx)
|
||||
// ctx = l.With().Str("foo", "bar").WithContext(ctx)
|
||||
func (l Logger) WithContext(ctx context.Context) context.Context {
|
||||
if lp, ok := ctx.Value(ctxKey{}).(*Logger); ok {
|
||||
// Update existing pointer.
|
||||
*lp = l
|
||||
return ctx
|
||||
}
|
||||
if l.level == Disabled {
|
||||
// Do not store disabled logger.
|
||||
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 {
|
||||
func Ctx(ctx context.Context) *Logger {
|
||||
if l, ok := ctx.Value(ctxKey{}).(*Logger); ok {
|
||||
return *l
|
||||
return l
|
||||
}
|
||||
return disabledLogger
|
||||
}
|
||||
|
|
3
vendor/github.com/rs/zerolog/globals.go
generated
vendored
3
vendor/github.com/rs/zerolog/globals.go
generated
vendored
|
@ -16,9 +16,6 @@ var (
|
|||
// 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.
|
||||
|
|
98
vendor/github.com/rs/zerolog/log.go
generated
vendored
98
vendor/github.com/rs/zerolog/log.go
generated
vendored
|
@ -62,17 +62,17 @@
|
|||
//
|
||||
// Sample logs:
|
||||
//
|
||||
// sampled := log.Sample(10)
|
||||
// sampled := log.Sample(&zerolog.BasicSampler{N: 10})
|
||||
// sampled.Info().Msg("will be logged every 10 messages")
|
||||
//
|
||||
package zerolog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/rs/zerolog/internal/json"
|
||||
)
|
||||
|
@ -115,15 +115,6 @@ func (l Level) String() string {
|
|||
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
|
||||
|
@ -134,8 +125,7 @@ var disabledEvent = newEvent(levelWriterAdapter{ioutil.Discard}, 0, false)
|
|||
type Logger struct {
|
||||
w LevelWriter
|
||||
level Level
|
||||
sample uint32
|
||||
counter *uint32
|
||||
sampler Sampler
|
||||
context []byte
|
||||
}
|
||||
|
||||
|
@ -162,6 +152,16 @@ func Nop() Logger {
|
|||
return New(nil).Level(Disabled)
|
||||
}
|
||||
|
||||
// Output duplicates the current logger and sets w as its output.
|
||||
func (l Logger) Output(w io.Writer) Logger {
|
||||
l2 := New(w)
|
||||
l2.level = l.level
|
||||
l2.sampler = l.sampler
|
||||
l2.context = make([]byte, len(l.context), cap(l.context))
|
||||
copy(l2.context, l.context)
|
||||
return l2
|
||||
}
|
||||
|
||||
// With creates a child logger with the field added to its context.
|
||||
func (l Logger) With() Context {
|
||||
context := l.context
|
||||
|
@ -175,34 +175,30 @@ func (l Logger) With() Context {
|
|||
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,
|
||||
// UpdateContext updates the internal logger's context.
|
||||
//
|
||||
// Use this method with caution. If unsure, prefer the With method.
|
||||
func (l *Logger) UpdateContext(update func(c Context) Context) {
|
||||
if l == disabledLogger {
|
||||
return
|
||||
}
|
||||
if cap(l.context) == 0 {
|
||||
l.context = make([]byte, 1, 500) // first byte is timestamp flag
|
||||
}
|
||||
c := update(Context{*l})
|
||||
l.context = c.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,
|
||||
}
|
||||
// Level creates a child logger with the minimum accepted level set to level.
|
||||
func (l Logger) Level(lvl Level) Logger {
|
||||
l.level = lvl
|
||||
return l
|
||||
}
|
||||
|
||||
// Sample returns a logger with the s sampler.
|
||||
func (l Logger) Sample(s Sampler) Logger {
|
||||
l.sampler = s
|
||||
return l
|
||||
}
|
||||
|
||||
// Debug starts a new message with debug level.
|
||||
|
@ -283,6 +279,22 @@ func (l Logger) Log() *Event {
|
|||
return l.newEvent(PanicLevel, false, nil)
|
||||
}
|
||||
|
||||
// Print sends a log event using debug level and no extra field.
|
||||
// Arguments are handled in the manner of fmt.Print.
|
||||
func (l Logger) Print(v ...interface{}) {
|
||||
if e := l.Debug(); e.Enabled() {
|
||||
e.Msg(fmt.Sprint(v...))
|
||||
}
|
||||
}
|
||||
|
||||
// Printf sends a log event using debug level and no extra field.
|
||||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l Logger) Printf(format string, v ...interface{}) {
|
||||
if e := l.Debug(); e.Enabled() {
|
||||
e.Msg(fmt.Sprintf(format, v...))
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
@ -304,7 +316,7 @@ func (l Logger) newEvent(level Level, addLevelField bool, done func(string)) *Ev
|
|||
if addLevelField {
|
||||
lvl = level
|
||||
}
|
||||
e := newEvent(l.w, lvl, enabled)
|
||||
e := newEvent(l.w, lvl, true)
|
||||
e.done = done
|
||||
if l.context != nil && len(l.context) > 0 && l.context[0] > 0 {
|
||||
// first byte of context is ts flag
|
||||
|
@ -313,9 +325,6 @@ func (l Logger) newEvent(level Level, addLevelField bool, done func(string)) *Ev
|
|||
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, ',')
|
||||
|
@ -330,9 +339,8 @@ 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
|
||||
if l.sampler != nil && !samplingDisabled() {
|
||||
return l.sampler.Sample(lvl)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
26
vendor/github.com/rs/zerolog/log/log.go
generated
vendored
26
vendor/github.com/rs/zerolog/log/log.go
generated
vendored
|
@ -3,6 +3,7 @@ package log
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
|
@ -11,6 +12,11 @@ import (
|
|||
// Logger is the global logger.
|
||||
var Logger = zerolog.New(os.Stderr).With().Timestamp().Logger()
|
||||
|
||||
// Output duplicates the global logger and sets w as its output.
|
||||
func Output(w io.Writer) zerolog.Logger {
|
||||
return Logger.Output(w)
|
||||
}
|
||||
|
||||
// With creates a child logger with the field added to its context.
|
||||
func With() zerolog.Context {
|
||||
return Logger.With()
|
||||
|
@ -21,9 +27,9 @@ 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)
|
||||
// Sample returns a logger with the s sampler.
|
||||
func Sample(s zerolog.Sampler) zerolog.Logger {
|
||||
return Logger.Sample(s)
|
||||
}
|
||||
|
||||
// Debug starts a new message with debug level.
|
||||
|
@ -78,8 +84,20 @@ func Log() *zerolog.Event {
|
|||
return Logger.Log()
|
||||
}
|
||||
|
||||
// Print sends a log event using debug level and no extra field.
|
||||
// Arguments are handled in the manner of fmt.Print.
|
||||
func Print(v ...interface{}) {
|
||||
Logger.Print(v...)
|
||||
}
|
||||
|
||||
// Printf sends a log event using debug level and no extra field.
|
||||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func Printf(format string, v ...interface{}) {
|
||||
Logger.Printf(format, v...)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
func Ctx(ctx context.Context) *zerolog.Logger {
|
||||
return zerolog.Ctx(ctx)
|
||||
}
|
||||
|
|
126
vendor/github.com/rs/zerolog/sampler.go
generated
vendored
Normal file
126
vendor/github.com/rs/zerolog/sampler.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
|||
package zerolog
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// Often samples log every ~ 10 events.
|
||||
Often = RandomSampler(10)
|
||||
// Sometimes samples log every ~ 100 events.
|
||||
Sometimes = RandomSampler(100)
|
||||
// Rarely samples log every ~ 1000 events.
|
||||
Rarely = RandomSampler(1000)
|
||||
)
|
||||
|
||||
// Sampler defines an interface to a log sampler.
|
||||
type Sampler interface {
|
||||
// Sample returns true if the event should be part of the sample, false if
|
||||
// the event should be dropped.
|
||||
Sample(lvl Level) bool
|
||||
}
|
||||
|
||||
// RandomSampler use a PRNG to randomly sample an event out of N events,
|
||||
// regardless of their level.
|
||||
type RandomSampler uint32
|
||||
|
||||
// Sample implements the Sampler interface.
|
||||
func (s RandomSampler) Sample(lvl Level) bool {
|
||||
if s <= 0 {
|
||||
return false
|
||||
}
|
||||
if rand.Intn(int(s)) != 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// BasicSampler is a sampler that will send every Nth events, regardless of
|
||||
// there level.
|
||||
type BasicSampler struct {
|
||||
N uint32
|
||||
counter uint32
|
||||
}
|
||||
|
||||
// Sample implements the Sampler interface.
|
||||
func (s *BasicSampler) Sample(lvl Level) bool {
|
||||
c := atomic.AddUint32(&s.counter, 1)
|
||||
return c%s.N == 0
|
||||
}
|
||||
|
||||
// BurstSampler lets Burst events pass per Period then pass the decision to
|
||||
// NextSampler. If Sampler is not set, all subsequent events are rejected.
|
||||
type BurstSampler struct {
|
||||
// Burst is the maximum number of event per period allowed before calling
|
||||
// NextSampler.
|
||||
Burst uint32
|
||||
// Period defines the burst period. If 0, NextSampler is always called.
|
||||
Period time.Duration
|
||||
// NextSampler is the sampler used after the burst is reached. If nil,
|
||||
// events are always rejected after the burst.
|
||||
NextSampler Sampler
|
||||
|
||||
counter uint32
|
||||
resetAt int64
|
||||
}
|
||||
|
||||
// Sample implements the Sampler interface.
|
||||
func (s *BurstSampler) Sample(lvl Level) bool {
|
||||
if s.Burst > 9 && s.Period > 0 {
|
||||
if s.inc() <= s.Burst {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if s.NextSampler == nil {
|
||||
return false
|
||||
}
|
||||
return s.NextSampler.Sample(lvl)
|
||||
}
|
||||
|
||||
func (s *BurstSampler) inc() uint32 {
|
||||
now := time.Now().UnixNano()
|
||||
resetAt := atomic.LoadInt64(&s.resetAt)
|
||||
var c uint32
|
||||
if now > resetAt {
|
||||
c = 1
|
||||
atomic.StoreUint32(&s.counter, c)
|
||||
newResetAt := now + s.Period.Nanoseconds()
|
||||
reset := atomic.CompareAndSwapInt64(&s.resetAt, resetAt, newResetAt)
|
||||
if !reset {
|
||||
// Lost the race with another goroutine trying to reset.
|
||||
c = atomic.AddUint32(&s.counter, 1)
|
||||
}
|
||||
} else {
|
||||
c = atomic.AddUint32(&s.counter, 1)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// LevelSampler applies a different sampler for each level.
|
||||
type LevelSampler struct {
|
||||
DebugSampler, InfoSampler, WarnSampler, ErrorSampler Sampler
|
||||
}
|
||||
|
||||
func (s LevelSampler) Sample(lvl Level) bool {
|
||||
switch lvl {
|
||||
case DebugLevel:
|
||||
if s.DebugSampler != nil {
|
||||
return s.DebugSampler.Sample(lvl)
|
||||
}
|
||||
case InfoLevel:
|
||||
if s.InfoSampler != nil {
|
||||
return s.InfoSampler.Sample(lvl)
|
||||
}
|
||||
case WarnLevel:
|
||||
if s.WarnSampler != nil {
|
||||
return s.WarnSampler.Sample(lvl)
|
||||
}
|
||||
case ErrorLevel:
|
||||
if s.ErrorSampler != nil {
|
||||
return s.ErrorSampler.Sample(lvl)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
8
vendor/vendor.json
vendored
8
vendor/vendor.json
vendored
|
@ -762,10 +762,10 @@
|
|||
"revisionTime": "2017-04-24T20:45:52Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "fGBeb3o1grSXGUNAjwptkBWfch0=",
|
||||
"checksumSHA1": "3Ie7HG2k47G/gwz8prjymTMLEms=",
|
||||
"path": "github.com/rs/zerolog",
|
||||
"revision": "89ff8dbc5f047ae9957523b07e627891079f7967",
|
||||
"revisionTime": "2017-07-27T06:42:12Z"
|
||||
"revision": "9d194eb6f50e8718a6d6f8f1e1f0bf3ddf4065f1",
|
||||
"revisionTime": "2017-09-11T21:52:32Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "AREhk6LKIp2I/4Njd756bqU6JSQ=",
|
||||
|
@ -774,7 +774,7 @@
|
|||
"revisionTime": "2017-07-27T06:42:12Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "kolarHDX6fkauW+1KWx1SFqSF2o=",
|
||||
"checksumSHA1": "VFgakVFNczJTf9gtUeIpkgcFRf0=",
|
||||
"path": "github.com/rs/zerolog/log",
|
||||
"revision": "89ff8dbc5f047ae9957523b07e627891079f7967",
|
||||
"revisionTime": "2017-07-27T06:42:12Z"
|
||||
|
|
Loading…
Reference in a new issue