diff --git a/bigquerydb/client.go b/bigquerydb/client.go index 0fc7feb0..2be2b317 100644 --- a/bigquerydb/client.go +++ b/bigquerydb/client.go @@ -18,6 +18,7 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "math" "os" "sort" @@ -25,11 +26,10 @@ import ( "time" "cloud.google.com/go/bigquery" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" + "github.com/prometheus/common/promslog" "github.com/prometheus/prometheus/prompb" "google.golang.org/api/iterator" "google.golang.org/api/option" @@ -37,7 +37,7 @@ import ( // BigqueryClient allows sending batches of Prometheus samples to Bigquery. type BigqueryClient struct { - logger log.Logger + logger *slog.Logger client bigquery.Client datasetID string tableID string @@ -50,13 +50,16 @@ type BigqueryClient struct { } // NewClient creates a new Client. -func NewClient(logger log.Logger, googleAPIjsonkeypath, googleProjectID, googleAPIdatasetID, googleAPItableID string, remoteTimeout time.Duration) *BigqueryClient { +func NewClient(logger *slog.Logger, googleAPIjsonkeypath, googleProjectID, googleAPIdatasetID, googleAPItableID string, remoteTimeout time.Duration) *BigqueryClient { ctx := context.Background() + if logger == nil { + logger = promslog.NewNopLogger() + } bigQueryClientOptions := []option.ClientOption{} if googleAPIjsonkeypath != "" { jsonFile, err := os.Open(googleAPIjsonkeypath) if err != nil { - level.Error(logger).Log("err", err) //nolint:errcheck + logger.Error("failed to open google api json key", slog.Any("error", err)) os.Exit(1) } @@ -65,7 +68,7 @@ func NewClient(logger log.Logger, googleAPIjsonkeypath, googleProjectID, googleA var result map[string]interface{} err = json.Unmarshal([]byte(byteValue), &result) if err != nil { - level.Error(logger).Log("err", err) //nolint:errcheck + logger.Error("failed to unmarshal google api json key", slog.Any("error", err)) os.Exit(1) } @@ -80,14 +83,10 @@ func NewClient(logger log.Logger, googleAPIjsonkeypath, googleProjectID, googleA c, err := bigquery.NewClient(ctx, googleProjectID, bigQueryClientOptions...) if err != nil { - level.Error(logger).Log("err", err) //nolint:errcheck + logger.Error("failed to create new bigquery client", slog.Any("error", err)) os.Exit(1) } - if logger == nil { - logger = log.NewNopLogger() - } - return &BigqueryClient{ logger: logger, client: *c, @@ -180,7 +179,7 @@ func (c *BigqueryClient) Write(timeseries []*prompb.TimeSeries) error { for _, s := range samples { v := float64(s.Value) if math.IsNaN(v) || math.IsInf(v, 0) { - //level.Debug(c.logger).Log("msg", "cannot send to BigQuery, skipping sample", "value", v, "sample", s) + c.logger.Debug("cannot send to bigquery, skipping sample", slog.Any("value", v), slog.Any("sample", s)) c.ignoredSamples.Inc() continue } @@ -259,7 +258,7 @@ func (c *BigqueryClient) Read(req *prompb.ReadRequest) (*prompb.ReadResponse, er } duration := time.Since(begin).Seconds() c.sqlQueryDuration.Observe(duration) - level.Debug(c.logger).Log("msg", "BigQuery SQL query", "rows", iter.TotalRows, "duration", duration) //nolint:errcheck + c.logger.Debug("bigquery sql query", slog.Any("rows", iter.TotalRows), slog.Any("duration", duration)) } resp := prompb.ReadResponse{ @@ -312,7 +311,7 @@ func (c *BigqueryClient) buildCommand(q *prompb.Query) (string, error) { matchers = append(matchers, fmt.Sprintf("timestamp <= TIMESTAMP_MILLIS(%v)", q.EndTimestampMs)) query := fmt.Sprintf("SELECT metricname, tags, UNIX_MILLIS(timestamp) as timestamp, value FROM %s.%s WHERE %v ORDER BY timestamp", c.datasetID, c.tableID, strings.Join(matchers, " AND ")) - level.Debug(c.logger).Log("msg", "BigQuery read", "sql query", query) //nolint:errcheck + c.logger.Debug("bigquery read", slog.Any("sql query", query)) return query, nil } diff --git a/bigquerydb/client_test.go b/bigquerydb/client_test.go index f11a5cd1..427d9bb3 100644 --- a/bigquerydb/client_test.go +++ b/bigquerydb/client_test.go @@ -16,18 +16,18 @@ limitations under the License. package bigquerydb import ( + "log/slog" "math" "os" "testing" "time" - "github.com/go-kit/log" "github.com/prometheus/prometheus/prompb" "github.com/stretchr/testify/assert" ) var bigQueryClientTimeout = time.Second * 60 -var logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) +var logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})) var googleAPIdatasetID = os.Getenv("BQ_DATASET_NAME") var googleAPItableID = os.Getenv("BQ_TABLE_NAME") diff --git a/go.mod b/go.mod index a4012a18..bed43e31 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.23 require ( cloud.google.com/go/bigquery v1.65.0 - github.com/go-kit/log v0.2.1 github.com/gogo/protobuf v1.3.2 github.com/golang/snappy v0.0.4 github.com/pkg/errors v0.9.1 @@ -29,7 +28,6 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/goccy/go-json v0.10.2 // indirect diff --git a/go.sum b/go.sum index 11881ac8..ad7ad6b4 100644 --- a/go.sum +++ b/go.sum @@ -43,10 +43,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= diff --git a/main.go b/main.go index 62392679..900c0ec0 100644 --- a/main.go +++ b/main.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "io" + "log/slog" "net/http" _ "net/http/pprof" "os" @@ -29,14 +30,12 @@ import ( "github.com/KohlsTechnology/prometheus_bigquery_remote_storage_adapter/bigquerydb" "github.com/KohlsTechnology/prometheus_bigquery_remote_storage_adapter/pkg/version" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/gogo/protobuf/proto" "github.com/golang/snappy" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/prometheus/common/promlog" + "github.com/prometheus/common/promslog" "github.com/prometheus/prometheus/prompb" "gopkg.in/alecthomas/kingpin.v2" ) @@ -49,7 +48,7 @@ type config struct { remoteTimeout time.Duration listenAddr string telemetryPath string - promlogConfig promlog.Config + promslogConfig promslog.Config printVersion bool } @@ -128,21 +127,21 @@ func main() { http.Handle(cfg.telemetryPath, promhttp.Handler()) - logger := promlog.New(&cfg.promlogConfig) + logger := promslog.New(&cfg.promslogConfig) - level.Info(logger).Log("msg", version.Get()) //nolint:errcheck + logger.Info(version.Get()) - level.Info(logger).Log("msg", "Configuration settings:", //nolint:errcheck - "googleAPIjsonkeypath", cfg.googleAPIjsonkeypath, - "googleProjectID", cfg.googleProjectID, - "googleAPIdatasetID", cfg.googleAPIdatasetID, - "googleAPItableID", cfg.googleAPItableID, - "telemetryPath", cfg.telemetryPath, - "listenAddr", cfg.listenAddr, - "remoteTimeout", cfg.remoteTimeout) + logger.Info("configuration settings", + slog.Any("googleAPIjsonkeypath", cfg.googleAPIjsonkeypath), + slog.Any("googleProjectID", cfg.googleProjectID), + slog.Any("googleAPIdatasetID", cfg.googleAPIdatasetID), + slog.Any("googleAPItableID", cfg.googleAPItableID), + slog.Any("telemetryPath", cfg.telemetryPath), + slog.Any("listenAddr", cfg.listenAddr), + slog.Any("remoteTimeout", cfg.remoteTimeout)) - writers, readers := buildClients(logger, cfg) - serve(logger, cfg.listenAddr, writers, readers) + writers, readers := buildClients(*logger, cfg) + serve(*logger, cfg.listenAddr, writers, readers) } func parseFlags() *config { @@ -150,7 +149,7 @@ func parseFlags() *config { a.HelpFlag.Short('h') cfg := &config{ - promlogConfig: promlog.Config{}, + promslogConfig: promslog.Config{}, } a.Flag("version", "Print version and build information, then exit"). @@ -170,12 +169,12 @@ func parseFlags() *config { Envar("PROMBQ_LISTEN").Default(":9201").StringVar(&cfg.listenAddr) a.Flag("web.telemetry-path", "Address to listen on for web endpoints."). Envar("PROMBQ_TELEMETRY").Default("/metrics").StringVar(&cfg.telemetryPath) - cfg.promlogConfig.Level = &promlog.AllowedLevel{} + cfg.promslogConfig.Level = &promslog.AllowedLevel{} a.Flag("log.level", "Only log messages with the given severity or above. One of: [debug, info, warn, error]"). - Envar("PROMBQ_LOG_LEVEL").Default("info").SetValue(cfg.promlogConfig.Level) - cfg.promlogConfig.Format = &promlog.AllowedFormat{} + Envar("PROMBQ_LOG_LEVEL").Default("info").SetValue(cfg.promslogConfig.Level) + cfg.promslogConfig.Format = &promslog.AllowedFormat{} a.Flag("log.format", "Output format of log messages. One of: [logfmt, json]"). - Envar("PROMBQ_LOG_FORMAT").Default("logfmt").SetValue(cfg.promlogConfig.Format) + Envar("PROMBQ_LOG_FORMAT").Default("logfmt").SetValue(cfg.promslogConfig.Format) _, err := a.Parse(os.Args[1:]) @@ -212,12 +211,12 @@ type reader interface { Name() string } -func buildClients(logger log.Logger, cfg *config) ([]writer, []reader) { +func buildClients(logger slog.Logger, cfg *config) ([]writer, []reader) { var writers []writer var readers []reader c := bigquerydb.NewClient( - log.With(logger, "storage", "BigQuery"), + logger.With("storage", "bigquery"), cfg.googleAPIjsonkeypath, cfg.googleProjectID, cfg.googleAPIdatasetID, @@ -226,11 +225,11 @@ func buildClients(logger log.Logger, cfg *config) ([]writer, []reader) { prometheus.MustRegister(c) writers = append(writers, c) readers = append(readers, c) - level.Info(logger).Log("msg", "Starting up...") //nolint:errcheck + logger.Info("starting up...") return writers, readers } -func serve(logger log.Logger, addr string, writers []writer, readers []reader) { +func serve(logger slog.Logger, addr string, writers []writer, readers []reader) { srv := &http.Server{ Addr: addr, } @@ -240,21 +239,21 @@ func serve(logger log.Logger, addr string, writers []writer, readers []reader) { sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt) oscall := <-sigChan - level.Warn(logger).Log("msg", "System Call Received stopping HTTP Server...", "SystemCall", oscall) //nolint:errcheck + logger.Warn("system call received stopping http server...", slog.Any("systemcall", oscall)) if err := srv.Shutdown(context.Background()); err != nil { - level.Error(logger).Log("msg", "Shutdown Error", "err", err) //nolint:errcheck + logger.Error("error while shutting down http server", slog.Any("error", err)) os.Exit(1) } close(idleConnectionClosed) - level.Warn(logger).Log("msg", "HTTP Server Shutdown, and connections closed") //nolint:errcheck + logger.Warn("http server shutdown, and connections closed") }() http.HandleFunc("/write", func(w http.ResponseWriter, r *http.Request) { - level.Debug(logger).Log("msg", "Request", "method", r.Method, "path", r.URL.Path) //nolint:errcheck + logger.Debug("write request received", slog.Any("method", r.Method), slog.Any("path", r.URL.Path)) begin := time.Now() compressed, err := io.ReadAll(r.Body) if err != nil { - level.Error(logger).Log("msg", "Read error", "err", err.Error()) //nolint:errcheck + logger.Error("read error", slog.Any("error", err.Error())) http.Error(w, err.Error(), http.StatusInternalServerError) writeErrors.Inc() return @@ -262,7 +261,7 @@ func serve(logger log.Logger, addr string, writers []writer, readers []reader) { reqBuf, err := snappy.Decode(nil, compressed) if err != nil { - level.Error(logger).Log("msg", "Decode error", "err", err.Error()) //nolint:errcheck + logger.Error("decode error", slog.Any("error", err.Error())) http.Error(w, err.Error(), http.StatusBadRequest) writeErrors.Inc() return @@ -270,7 +269,7 @@ func serve(logger log.Logger, addr string, writers []writer, readers []reader) { var req prompb.WriteRequest if err := proto.Unmarshal(reqBuf, &req); err != nil { - level.Error(logger).Log("msg", "Unmarshal error", "err", err.Error()) //nolint:errcheck + logger.Error("unmarshal error", slog.Any("error", err.Error())) http.Error(w, err.Error(), http.StatusBadRequest) writeErrors.Inc() return @@ -288,16 +287,16 @@ func serve(logger log.Logger, addr string, writers []writer, readers []reader) { duration := time.Since(begin).Seconds() writeProcessingDuration.WithLabelValues(writers[0].Name()).Observe(duration) - level.Debug(logger).Log("msg", "/write", "duration", duration) //nolint:errcheck + logger.Debug("write request completed", slog.Any("duration", duration)) }) http.HandleFunc("/read", func(w http.ResponseWriter, r *http.Request) { - level.Debug(logger).Log("msg", "Request", "method", r.Method, "path", r.URL.Path) //nolint:errcheck + logger.Debug("read request receieved", slog.Any("method", r.Method), slog.Any("path", r.URL.Path)) begin := time.Now() compressed, err := io.ReadAll(r.Body) if err != nil { - level.Error(logger).Log("msg", "Read error", "err", err.Error()) //nolint:errcheck + logger.Error("read error", slog.Any("error", err.Error())) http.Error(w, err.Error(), http.StatusInternalServerError) readErrors.Inc() return @@ -305,7 +304,7 @@ func serve(logger log.Logger, addr string, writers []writer, readers []reader) { reqBuf, err := snappy.Decode(nil, compressed) if err != nil { - level.Error(logger).Log("msg", "Decode error", "err", err.Error()) //nolint:errcheck + logger.Error("decode error", slog.Any("error", err.Error())) http.Error(w, err.Error(), http.StatusBadRequest) readErrors.Inc() return @@ -313,7 +312,7 @@ func serve(logger log.Logger, addr string, writers []writer, readers []reader) { var req prompb.ReadRequest if err := proto.Unmarshal(reqBuf, &req); err != nil { - level.Error(logger).Log("msg", "Unmarshal error", "err", err.Error()) //nolint:errcheck + logger.Error("unmarshal error", slog.Any("error", err.Error())) http.Error(w, err.Error(), http.StatusBadRequest) readErrors.Inc() return @@ -330,7 +329,7 @@ func serve(logger log.Logger, addr string, writers []writer, readers []reader) { var resp *prompb.ReadResponse resp, err = reader.Read(&req) if err != nil { - level.Warn(logger).Log("msg", "Error executing query", "query", req, "storage", reader.Name(), "err", err) //nolint:errcheck + logger.Warn("error executing query", slog.Any("query", req), slog.Any("storage", reader.Name()), slog.Any("error", err)) http.Error(w, err.Error(), http.StatusInternalServerError) readErrors.Inc() return @@ -348,32 +347,32 @@ func serve(logger log.Logger, addr string, writers []writer, readers []reader) { compressed = snappy.Encode(nil, data) if _, err := w.Write(compressed); err != nil { - level.Warn(logger).Log("msg", "Error writing response", "storage", reader.Name(), "err", err) //nolint:errcheck + logger.Warn("error writing response", slog.Any("storage", reader.Name()), slog.Any("error", err)) readErrors.Inc() } duration := time.Since(begin).Seconds() readProcessingDuration.WithLabelValues(writers[0].Name()).Observe(duration) - level.Debug(logger).Log("msg", "/read", "duration", duration) //nolint:errcheck + logger.Debug("read request completed", slog.Any("duration", duration)) }) if err := srv.ListenAndServe(); err != http.ErrServerClosed { - level.Error(logger).Log("msg", "Failed to listen", "addr", addr, "err", err) //nolint:errcheck + logger.Error("failed to listen", slog.Any("addr", addr), slog.Any("error", err)) os.Exit(1) } <-idleConnectionClosed } -func sendSamples(logger log.Logger, w writer, timeseries []*prompb.TimeSeries) { +func sendSamples(logger slog.Logger, w writer, timeseries []*prompb.TimeSeries) { begin := time.Now() err := w.Write(timeseries) duration := time.Since(begin).Seconds() if err != nil { - level.Warn(logger).Log("msg", "Error sending samples to remote storage", "err", err, "storage", w.Name(), "num_samples", len(timeseries)) //nolint:errcheck + logger.Warn("error sending samples to remote storage", slog.Any("error", err), slog.Any("storage", w.Name()), slog.Any("num_samples", len(timeseries))) failedSamples.WithLabelValues(w.Name()).Add(float64(len(timeseries))) writeErrors.Inc() } else { - level.Debug(logger).Log("msg", "Sent samples", "num_samples", len(timeseries)) //nolint:errcheck + logger.Debug("sent samples", slog.Any("num_samples", len(timeseries))) sentSamples.WithLabelValues(w.Name()).Add(float64(len(timeseries))) sentBatchDuration.WithLabelValues(w.Name()).Observe(duration) } diff --git a/vendor/github.com/go-kit/log/.gitignore b/vendor/github.com/go-kit/log/.gitignore deleted file mode 100644 index 66fd13c9..00000000 --- a/vendor/github.com/go-kit/log/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ diff --git a/vendor/github.com/go-kit/log/LICENSE b/vendor/github.com/go-kit/log/LICENSE deleted file mode 100644 index bb5bdb9c..00000000 --- a/vendor/github.com/go-kit/log/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 Go kit - -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/go-kit/log/README.md b/vendor/github.com/go-kit/log/README.md deleted file mode 100644 index 80677946..00000000 --- a/vendor/github.com/go-kit/log/README.md +++ /dev/null @@ -1,156 +0,0 @@ -# package log - -[![Go Reference](https://pkg.go.dev/badge/github.com/go-kit/log.svg)](https://pkg.go.dev/github.com/go-kit/log) -[![Go Report Card](https://goreportcard.com/badge/go-kit/log)](https://goreportcard.com/report/go-kit/log) -[![GitHub Actions](https://github.com/go-kit/log/actions/workflows/test.yml/badge.svg)](https://github.com/go-kit/log/actions/workflows/test.yml) -[![Coverage Status](https://coveralls.io/repos/github/go-kit/log/badge.svg?branch=main)](https://coveralls.io/github/go-kit/log?branch=main) - -`package log` provides a minimal interface for structured logging in services. -It may be wrapped to encode conventions, enforce type-safety, provide leveled -logging, and so on. It can be used for both typical application log events, -and log-structured data streams. - -## Structured logging - -Structured logging is, basically, conceding to the reality that logs are -_data_, and warrant some level of schematic rigor. Using a stricter, -key/value-oriented message format for our logs, containing contextual and -semantic information, makes it much easier to get insight into the -operational activity of the systems we build. Consequently, `package log` is -of the strong belief that "[the benefits of structured logging outweigh the -minimal effort involved](https://www.thoughtworks.com/radar/techniques/structured-logging)". - -Migrating from unstructured to structured logging is probably a lot easier -than you'd expect. - -```go -// Unstructured -log.Printf("HTTP server listening on %s", addr) - -// Structured -logger.Log("transport", "HTTP", "addr", addr, "msg", "listening") -``` - -## Usage - -### Typical application logging - -```go -w := log.NewSyncWriter(os.Stderr) -logger := log.NewLogfmtLogger(w) -logger.Log("question", "what is the meaning of life?", "answer", 42) - -// Output: -// question="what is the meaning of life?" answer=42 -``` - -### Contextual Loggers - -```go -func main() { - var logger log.Logger - logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) - logger = log.With(logger, "instance_id", 123) - - logger.Log("msg", "starting") - NewWorker(log.With(logger, "component", "worker")).Run() - NewSlacker(log.With(logger, "component", "slacker")).Run() -} - -// Output: -// instance_id=123 msg=starting -// instance_id=123 component=worker msg=running -// instance_id=123 component=slacker msg=running -``` - -### Interact with stdlib logger - -Redirect stdlib logger to Go kit logger. - -```go -import ( - "os" - stdlog "log" - kitlog "github.com/go-kit/log" -) - -func main() { - logger := kitlog.NewJSONLogger(kitlog.NewSyncWriter(os.Stdout)) - stdlog.SetOutput(kitlog.NewStdlibAdapter(logger)) - stdlog.Print("I sure like pie") -} - -// Output: -// {"msg":"I sure like pie","ts":"2016/01/01 12:34:56"} -``` - -Or, if, for legacy reasons, you need to pipe all of your logging through the -stdlib log package, you can redirect Go kit logger to the stdlib logger. - -```go -logger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{}) -logger.Log("legacy", true, "msg", "at least it's something") - -// Output: -// 2016/01/01 12:34:56 legacy=true msg="at least it's something" -``` - -### Timestamps and callers - -```go -var logger log.Logger -logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) -logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) - -logger.Log("msg", "hello") - -// Output: -// ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello -``` - -## Levels - -Log levels are supported via the [level package](https://godoc.org/github.com/go-kit/log/level). - -## Supported output formats - -- [Logfmt](https://brandur.org/logfmt) ([see also](https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write)) -- JSON - -## Enhancements - -`package log` is centered on the one-method Logger interface. - -```go -type Logger interface { - Log(keyvals ...interface{}) error -} -``` - -This interface, and its supporting code like is the product of much iteration -and evaluation. For more details on the evolution of the Logger interface, -see [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1), -a talk by [Chris Hines](https://github.com/ChrisHines). -Also, please see -[#63](https://github.com/go-kit/kit/issues/63), -[#76](https://github.com/go-kit/kit/pull/76), -[#131](https://github.com/go-kit/kit/issues/131), -[#157](https://github.com/go-kit/kit/pull/157), -[#164](https://github.com/go-kit/kit/issues/164), and -[#252](https://github.com/go-kit/kit/pull/252) -to review historical conversations about package log and the Logger interface. - -Value-add packages and suggestions, -like improvements to [the leveled logger](https://godoc.org/github.com/go-kit/log/level), -are of course welcome. Good proposals should - -- Be composable with [contextual loggers](https://godoc.org/github.com/go-kit/log#With), -- Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/log#Caller) in any wrapped contextual loggers, and -- Be friendly to packages that accept only an unadorned log.Logger. - -## Benchmarks & comparisons - -There are a few Go logging benchmarks and comparisons that include Go kit's package log. - -- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log -- [uber-common/zap](https://github.com/uber-common/zap), a zero-alloc logging library, includes a comparison with kit/log diff --git a/vendor/github.com/go-kit/log/doc.go b/vendor/github.com/go-kit/log/doc.go deleted file mode 100644 index f744382f..00000000 --- a/vendor/github.com/go-kit/log/doc.go +++ /dev/null @@ -1,116 +0,0 @@ -// Package log provides a structured logger. -// -// Structured logging produces logs easily consumed later by humans or -// machines. Humans might be interested in debugging errors, or tracing -// specific requests. Machines might be interested in counting interesting -// events, or aggregating information for off-line processing. In both cases, -// it is important that the log messages are structured and actionable. -// Package log is designed to encourage both of these best practices. -// -// Basic Usage -// -// The fundamental interface is Logger. Loggers create log events from -// key/value data. The Logger interface has a single method, Log, which -// accepts a sequence of alternating key/value pairs, which this package names -// keyvals. -// -// type Logger interface { -// Log(keyvals ...interface{}) error -// } -// -// Here is an example of a function using a Logger to create log events. -// -// func RunTask(task Task, logger log.Logger) string { -// logger.Log("taskID", task.ID, "event", "starting task") -// ... -// logger.Log("taskID", task.ID, "event", "task complete") -// } -// -// The keys in the above example are "taskID" and "event". The values are -// task.ID, "starting task", and "task complete". Every key is followed -// immediately by its value. -// -// Keys are usually plain strings. Values may be any type that has a sensible -// encoding in the chosen log format. With structured logging it is a good -// idea to log simple values without formatting them. This practice allows -// the chosen logger to encode values in the most appropriate way. -// -// Contextual Loggers -// -// A contextual logger stores keyvals that it includes in all log events. -// Building appropriate contextual loggers reduces repetition and aids -// consistency in the resulting log output. With, WithPrefix, and WithSuffix -// add context to a logger. We can use With to improve the RunTask example. -// -// func RunTask(task Task, logger log.Logger) string { -// logger = log.With(logger, "taskID", task.ID) -// logger.Log("event", "starting task") -// ... -// taskHelper(task.Cmd, logger) -// ... -// logger.Log("event", "task complete") -// } -// -// The improved version emits the same log events as the original for the -// first and last calls to Log. Passing the contextual logger to taskHelper -// enables each log event created by taskHelper to include the task.ID even -// though taskHelper does not have access to that value. Using contextual -// loggers this way simplifies producing log output that enables tracing the -// life cycle of individual tasks. (See the Contextual example for the full -// code of the above snippet.) -// -// Dynamic Contextual Values -// -// A Valuer function stored in a contextual logger generates a new value each -// time an event is logged. The Valuer example demonstrates how this feature -// works. -// -// Valuers provide the basis for consistently logging timestamps and source -// code location. The log package defines several valuers for that purpose. -// See Timestamp, DefaultTimestamp, DefaultTimestampUTC, Caller, and -// DefaultCaller. A common logger initialization sequence that ensures all log -// entries contain a timestamp and source location looks like this: -// -// logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) -// logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) -// -// Concurrent Safety -// -// Applications with multiple goroutines want each log event written to the -// same logger to remain separate from other log events. Package log provides -// two simple solutions for concurrent safe logging. -// -// NewSyncWriter wraps an io.Writer and serializes each call to its Write -// method. Using a SyncWriter has the benefit that the smallest practical -// portion of the logging logic is performed within a mutex, but it requires -// the formatting Logger to make only one call to Write per log event. -// -// NewSyncLogger wraps any Logger and serializes each call to its Log method. -// Using a SyncLogger has the benefit that it guarantees each log event is -// handled atomically within the wrapped logger, but it typically serializes -// both the formatting and output logic. Use a SyncLogger if the formatting -// logger may perform multiple writes per log event. -// -// Error Handling -// -// This package relies on the practice of wrapping or decorating loggers with -// other loggers to provide composable pieces of functionality. It also means -// that Logger.Log must return an error because some -// implementations—especially those that output log data to an io.Writer—may -// encounter errors that cannot be handled locally. This in turn means that -// Loggers that wrap other loggers should return errors from the wrapped -// logger up the stack. -// -// Fortunately, the decorator pattern also provides a way to avoid the -// necessity to check for errors every time an application calls Logger.Log. -// An application required to panic whenever its Logger encounters -// an error could initialize its logger as follows. -// -// fmtlogger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) -// logger := log.LoggerFunc(func(keyvals ...interface{}) error { -// if err := fmtlogger.Log(keyvals...); err != nil { -// panic(err) -// } -// return nil -// }) -package log diff --git a/vendor/github.com/go-kit/log/json_logger.go b/vendor/github.com/go-kit/log/json_logger.go deleted file mode 100644 index d0faed4f..00000000 --- a/vendor/github.com/go-kit/log/json_logger.go +++ /dev/null @@ -1,91 +0,0 @@ -package log - -import ( - "encoding" - "encoding/json" - "fmt" - "io" - "reflect" -) - -type jsonLogger struct { - io.Writer -} - -// NewJSONLogger returns a Logger that encodes keyvals to the Writer as a -// single JSON object. Each log event produces no more than one call to -// w.Write. The passed Writer must be safe for concurrent use by multiple -// goroutines if the returned Logger will be used concurrently. -func NewJSONLogger(w io.Writer) Logger { - return &jsonLogger{w} -} - -func (l *jsonLogger) Log(keyvals ...interface{}) error { - n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd - m := make(map[string]interface{}, n) - for i := 0; i < len(keyvals); i += 2 { - k := keyvals[i] - var v interface{} = ErrMissingValue - if i+1 < len(keyvals) { - v = keyvals[i+1] - } - merge(m, k, v) - } - enc := json.NewEncoder(l.Writer) - enc.SetEscapeHTML(false) - return enc.Encode(m) -} - -func merge(dst map[string]interface{}, k, v interface{}) { - var key string - switch x := k.(type) { - case string: - key = x - case fmt.Stringer: - key = safeString(x) - default: - key = fmt.Sprint(x) - } - - // We want json.Marshaler and encoding.TextMarshaller to take priority over - // err.Error() and v.String(). But json.Marshall (called later) does that by - // default so we force a no-op if it's one of those 2 case. - switch x := v.(type) { - case json.Marshaler: - case encoding.TextMarshaler: - case error: - v = safeError(x) - case fmt.Stringer: - v = safeString(x) - } - - dst[key] = v -} - -func safeString(str fmt.Stringer) (s string) { - defer func() { - if panicVal := recover(); panicVal != nil { - if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() { - s = "NULL" - } else { - s = fmt.Sprintf("PANIC in String method: %v", panicVal) - } - } - }() - s = str.String() - return -} - -func safeError(err error) (s interface{}) { - defer func() { - if panicVal := recover(); panicVal != nil { - if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { - s = nil - } else { - s = fmt.Sprintf("PANIC in Error method: %v", panicVal) - } - } - }() - s = err.Error() - return -} diff --git a/vendor/github.com/go-kit/log/level/doc.go b/vendor/github.com/go-kit/log/level/doc.go deleted file mode 100644 index fd681dcf..00000000 --- a/vendor/github.com/go-kit/log/level/doc.go +++ /dev/null @@ -1,33 +0,0 @@ -// Package level implements leveled logging on top of Go kit's log package. To -// use the level package, create a logger as per normal in your func main, and -// wrap it with level.NewFilter. -// -// var logger log.Logger -// logger = log.NewLogfmtLogger(os.Stderr) -// logger = level.NewFilter(logger, level.AllowInfo()) // <-- -// logger = log.With(logger, "ts", log.DefaultTimestampUTC) -// -// It's also possible to configure log level from a string. For instance from -// a flag, environment variable or configuration file. -// -// fs := flag.NewFlagSet("myprogram") -// lvl := fs.String("log", "info", "debug, info, warn, error") -// -// var logger log.Logger -// logger = log.NewLogfmtLogger(os.Stderr) -// logger = level.NewFilter(logger, level.Allow(level.ParseDefault(*lvl, level.InfoValue()))) // <-- -// logger = log.With(logger, "ts", log.DefaultTimestampUTC) -// -// Then, at the callsites, use one of the level.Debug, Info, Warn, or Error -// helper methods to emit leveled log events. -// -// logger.Log("foo", "bar") // as normal, no level -// level.Debug(logger).Log("request_id", reqID, "trace_data", trace.Get()) -// if value > 100 { -// level.Error(logger).Log("value", value) -// } -// -// NewFilter allows precise control over what happens when a log event is -// emitted without a level key, or if a squelched level is used. Check the -// Option functions for details. -package level diff --git a/vendor/github.com/go-kit/log/level/level.go b/vendor/github.com/go-kit/log/level/level.go deleted file mode 100644 index c641d985..00000000 --- a/vendor/github.com/go-kit/log/level/level.go +++ /dev/null @@ -1,256 +0,0 @@ -package level - -import ( - "errors" - "strings" - - "github.com/go-kit/log" -) - -// ErrInvalidLevelString is returned whenever an invalid string is passed to Parse. -var ErrInvalidLevelString = errors.New("invalid level string") - -// Error returns a logger that includes a Key/ErrorValue pair. -func Error(logger log.Logger) log.Logger { - return log.WithPrefix(logger, Key(), ErrorValue()) -} - -// Warn returns a logger that includes a Key/WarnValue pair. -func Warn(logger log.Logger) log.Logger { - return log.WithPrefix(logger, Key(), WarnValue()) -} - -// Info returns a logger that includes a Key/InfoValue pair. -func Info(logger log.Logger) log.Logger { - return log.WithPrefix(logger, Key(), InfoValue()) -} - -// Debug returns a logger that includes a Key/DebugValue pair. -func Debug(logger log.Logger) log.Logger { - return log.WithPrefix(logger, Key(), DebugValue()) -} - -// NewFilter wraps next and implements level filtering. See the commentary on -// the Option functions for a detailed description of how to configure levels. -// If no options are provided, all leveled log events created with Debug, -// Info, Warn or Error helper methods are squelched and non-leveled log -// events are passed to next unmodified. -func NewFilter(next log.Logger, options ...Option) log.Logger { - l := &logger{ - next: next, - } - for _, option := range options { - option(l) - } - return l -} - -type logger struct { - next log.Logger - allowed level - squelchNoLevel bool - errNotAllowed error - errNoLevel error -} - -func (l *logger) Log(keyvals ...interface{}) error { - var hasLevel, levelAllowed bool - for i := 1; i < len(keyvals); i += 2 { - if v, ok := keyvals[i].(*levelValue); ok { - hasLevel = true - levelAllowed = l.allowed&v.level != 0 - break - } - } - if !hasLevel && l.squelchNoLevel { - return l.errNoLevel - } - if hasLevel && !levelAllowed { - return l.errNotAllowed - } - return l.next.Log(keyvals...) -} - -// Option sets a parameter for the leveled logger. -type Option func(*logger) - -// Allow the provided log level to pass. -func Allow(v Value) Option { - switch v { - case debugValue: - return AllowDebug() - case infoValue: - return AllowInfo() - case warnValue: - return AllowWarn() - case errorValue: - return AllowError() - default: - return AllowNone() - } -} - -// AllowAll is an alias for AllowDebug. -func AllowAll() Option { - return AllowDebug() -} - -// AllowDebug allows error, warn, info and debug level log events to pass. -func AllowDebug() Option { - return allowed(levelError | levelWarn | levelInfo | levelDebug) -} - -// AllowInfo allows error, warn and info level log events to pass. -func AllowInfo() Option { - return allowed(levelError | levelWarn | levelInfo) -} - -// AllowWarn allows error and warn level log events to pass. -func AllowWarn() Option { - return allowed(levelError | levelWarn) -} - -// AllowError allows only error level log events to pass. -func AllowError() Option { - return allowed(levelError) -} - -// AllowNone allows no leveled log events to pass. -func AllowNone() Option { - return allowed(0) -} - -func allowed(allowed level) Option { - return func(l *logger) { l.allowed = allowed } -} - -// Parse a string to its corresponding level value. Valid strings are "debug", -// "info", "warn", and "error". Strings are normalized via strings.TrimSpace and -// strings.ToLower. -func Parse(level string) (Value, error) { - switch strings.TrimSpace(strings.ToLower(level)) { - case debugValue.name: - return debugValue, nil - case infoValue.name: - return infoValue, nil - case warnValue.name: - return warnValue, nil - case errorValue.name: - return errorValue, nil - default: - return nil, ErrInvalidLevelString - } -} - -// ParseDefault calls Parse and returns the default Value on error. -func ParseDefault(level string, def Value) Value { - v, err := Parse(level) - if err != nil { - return def - } - return v -} - -// ErrNotAllowed sets the error to return from Log when it squelches a log -// event disallowed by the configured Allow[Level] option. By default, -// ErrNotAllowed is nil; in this case the log event is squelched with no -// error. -func ErrNotAllowed(err error) Option { - return func(l *logger) { l.errNotAllowed = err } -} - -// SquelchNoLevel instructs Log to squelch log events with no level, so that -// they don't proceed through to the wrapped logger. If SquelchNoLevel is set -// to true and a log event is squelched in this way, the error value -// configured with ErrNoLevel is returned to the caller. -func SquelchNoLevel(squelch bool) Option { - return func(l *logger) { l.squelchNoLevel = squelch } -} - -// ErrNoLevel sets the error to return from Log when it squelches a log event -// with no level. By default, ErrNoLevel is nil; in this case the log event is -// squelched with no error. -func ErrNoLevel(err error) Option { - return func(l *logger) { l.errNoLevel = err } -} - -// NewInjector wraps next and returns a logger that adds a Key/level pair to -// the beginning of log events that don't already contain a level. In effect, -// this gives a default level to logs without a level. -func NewInjector(next log.Logger, level Value) log.Logger { - return &injector{ - next: next, - level: level, - } -} - -type injector struct { - next log.Logger - level interface{} -} - -func (l *injector) Log(keyvals ...interface{}) error { - for i := 1; i < len(keyvals); i += 2 { - if _, ok := keyvals[i].(*levelValue); ok { - return l.next.Log(keyvals...) - } - } - kvs := make([]interface{}, len(keyvals)+2) - kvs[0], kvs[1] = key, l.level - copy(kvs[2:], keyvals) - return l.next.Log(kvs...) -} - -// Value is the interface that each of the canonical level values implement. -// It contains unexported methods that prevent types from other packages from -// implementing it and guaranteeing that NewFilter can distinguish the levels -// defined in this package from all other values. -type Value interface { - String() string - levelVal() -} - -// Key returns the unique key added to log events by the loggers in this -// package. -func Key() interface{} { return key } - -// ErrorValue returns the unique value added to log events by Error. -func ErrorValue() Value { return errorValue } - -// WarnValue returns the unique value added to log events by Warn. -func WarnValue() Value { return warnValue } - -// InfoValue returns the unique value added to log events by Info. -func InfoValue() Value { return infoValue } - -// DebugValue returns the unique value added to log events by Debug. -func DebugValue() Value { return debugValue } - -var ( - // key is of type interface{} so that it allocates once during package - // initialization and avoids allocating every time the value is added to a - // []interface{} later. - key interface{} = "level" - - errorValue = &levelValue{level: levelError, name: "error"} - warnValue = &levelValue{level: levelWarn, name: "warn"} - infoValue = &levelValue{level: levelInfo, name: "info"} - debugValue = &levelValue{level: levelDebug, name: "debug"} -) - -type level byte - -const ( - levelDebug level = 1 << iota - levelInfo - levelWarn - levelError -) - -type levelValue struct { - name string - level -} - -func (v *levelValue) String() string { return v.name } -func (v *levelValue) levelVal() {} diff --git a/vendor/github.com/go-kit/log/log.go b/vendor/github.com/go-kit/log/log.go deleted file mode 100644 index 62e11ada..00000000 --- a/vendor/github.com/go-kit/log/log.go +++ /dev/null @@ -1,179 +0,0 @@ -package log - -import "errors" - -// Logger is the fundamental interface for all log operations. Log creates a -// log event from keyvals, a variadic sequence of alternating keys and values. -// Implementations must be safe for concurrent use by multiple goroutines. In -// particular, any implementation of Logger that appends to keyvals or -// modifies or retains any of its elements must make a copy first. -type Logger interface { - Log(keyvals ...interface{}) error -} - -// ErrMissingValue is appended to keyvals slices with odd length to substitute -// the missing value. -var ErrMissingValue = errors.New("(MISSING)") - -// With returns a new contextual logger with keyvals prepended to those passed -// to calls to Log. If logger is also a contextual logger created by With, -// WithPrefix, or WithSuffix, keyvals is appended to the existing context. -// -// The returned Logger replaces all value elements (odd indexes) containing a -// Valuer with their generated value for each call to its Log method. -func With(logger Logger, keyvals ...interface{}) Logger { - if len(keyvals) == 0 { - return logger - } - l := newContext(logger) - kvs := append(l.keyvals, keyvals...) - if len(kvs)%2 != 0 { - kvs = append(kvs, ErrMissingValue) - } - return &context{ - logger: l.logger, - // Limiting the capacity of the stored keyvals ensures that a new - // backing array is created if the slice must grow in Log or With. - // Using the extra capacity without copying risks a data race that - // would violate the Logger interface contract. - keyvals: kvs[:len(kvs):len(kvs)], - hasValuer: l.hasValuer || containsValuer(keyvals), - sKeyvals: l.sKeyvals, - sHasValuer: l.sHasValuer, - } -} - -// WithPrefix returns a new contextual logger with keyvals prepended to those -// passed to calls to Log. If logger is also a contextual logger created by -// With, WithPrefix, or WithSuffix, keyvals is prepended to the existing context. -// -// The returned Logger replaces all value elements (odd indexes) containing a -// Valuer with their generated value for each call to its Log method. -func WithPrefix(logger Logger, keyvals ...interface{}) Logger { - if len(keyvals) == 0 { - return logger - } - l := newContext(logger) - // Limiting the capacity of the stored keyvals ensures that a new - // backing array is created if the slice must grow in Log or With. - // Using the extra capacity without copying risks a data race that - // would violate the Logger interface contract. - n := len(l.keyvals) + len(keyvals) - if len(keyvals)%2 != 0 { - n++ - } - kvs := make([]interface{}, 0, n) - kvs = append(kvs, keyvals...) - if len(kvs)%2 != 0 { - kvs = append(kvs, ErrMissingValue) - } - kvs = append(kvs, l.keyvals...) - return &context{ - logger: l.logger, - keyvals: kvs, - hasValuer: l.hasValuer || containsValuer(keyvals), - sKeyvals: l.sKeyvals, - sHasValuer: l.sHasValuer, - } -} - -// WithSuffix returns a new contextual logger with keyvals appended to those -// passed to calls to Log. If logger is also a contextual logger created by -// With, WithPrefix, or WithSuffix, keyvals is appended to the existing context. -// -// The returned Logger replaces all value elements (odd indexes) containing a -// Valuer with their generated value for each call to its Log method. -func WithSuffix(logger Logger, keyvals ...interface{}) Logger { - if len(keyvals) == 0 { - return logger - } - l := newContext(logger) - // Limiting the capacity of the stored keyvals ensures that a new - // backing array is created if the slice must grow in Log or With. - // Using the extra capacity without copying risks a data race that - // would violate the Logger interface contract. - n := len(l.sKeyvals) + len(keyvals) - if len(keyvals)%2 != 0 { - n++ - } - kvs := make([]interface{}, 0, n) - kvs = append(kvs, keyvals...) - if len(kvs)%2 != 0 { - kvs = append(kvs, ErrMissingValue) - } - kvs = append(l.sKeyvals, kvs...) - return &context{ - logger: l.logger, - keyvals: l.keyvals, - hasValuer: l.hasValuer, - sKeyvals: kvs, - sHasValuer: l.sHasValuer || containsValuer(keyvals), - } -} - -// context is the Logger implementation returned by With, WithPrefix, and -// WithSuffix. It wraps a Logger and holds keyvals that it includes in all -// log events. Its Log method calls bindValues to generate values for each -// Valuer in the context keyvals. -// -// A context must always have the same number of stack frames between calls to -// its Log method and the eventual binding of Valuers to their value. This -// requirement comes from the functional requirement to allow a context to -// resolve application call site information for a Caller stored in the -// context. To do this we must be able to predict the number of logging -// functions on the stack when bindValues is called. -// -// Two implementation details provide the needed stack depth consistency. -// -// 1. newContext avoids introducing an additional layer when asked to -// wrap another context. -// 2. With, WithPrefix, and WithSuffix avoid introducing an additional -// layer by returning a newly constructed context with a merged keyvals -// rather than simply wrapping the existing context. -type context struct { - logger Logger - keyvals []interface{} - sKeyvals []interface{} // suffixes - hasValuer bool - sHasValuer bool -} - -func newContext(logger Logger) *context { - if c, ok := logger.(*context); ok { - return c - } - return &context{logger: logger} -} - -// Log replaces all value elements (odd indexes) containing a Valuer in the -// stored context with their generated value, appends keyvals, and passes the -// result to the wrapped Logger. -func (l *context) Log(keyvals ...interface{}) error { - kvs := append(l.keyvals, keyvals...) - if len(kvs)%2 != 0 { - kvs = append(kvs, ErrMissingValue) - } - if l.hasValuer { - // If no keyvals were appended above then we must copy l.keyvals so - // that future log events will reevaluate the stored Valuers. - if len(keyvals) == 0 { - kvs = append([]interface{}{}, l.keyvals...) - } - bindValues(kvs[:(len(l.keyvals))]) - } - kvs = append(kvs, l.sKeyvals...) - if l.sHasValuer { - bindValues(kvs[len(kvs)-len(l.sKeyvals):]) - } - return l.logger.Log(kvs...) -} - -// LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If -// f is a function with the appropriate signature, LoggerFunc(f) is a Logger -// object that calls f. -type LoggerFunc func(...interface{}) error - -// Log implements Logger by calling f(keyvals...). -func (f LoggerFunc) Log(keyvals ...interface{}) error { - return f(keyvals...) -} diff --git a/vendor/github.com/go-kit/log/logfmt_logger.go b/vendor/github.com/go-kit/log/logfmt_logger.go deleted file mode 100644 index a0030529..00000000 --- a/vendor/github.com/go-kit/log/logfmt_logger.go +++ /dev/null @@ -1,62 +0,0 @@ -package log - -import ( - "bytes" - "io" - "sync" - - "github.com/go-logfmt/logfmt" -) - -type logfmtEncoder struct { - *logfmt.Encoder - buf bytes.Buffer -} - -func (l *logfmtEncoder) Reset() { - l.Encoder.Reset() - l.buf.Reset() -} - -var logfmtEncoderPool = sync.Pool{ - New: func() interface{} { - var enc logfmtEncoder - enc.Encoder = logfmt.NewEncoder(&enc.buf) - return &enc - }, -} - -type logfmtLogger struct { - w io.Writer -} - -// NewLogfmtLogger returns a logger that encodes keyvals to the Writer in -// logfmt format. Each log event produces no more than one call to w.Write. -// The passed Writer must be safe for concurrent use by multiple goroutines if -// the returned Logger will be used concurrently. -func NewLogfmtLogger(w io.Writer) Logger { - return &logfmtLogger{w} -} - -func (l logfmtLogger) Log(keyvals ...interface{}) error { - enc := logfmtEncoderPool.Get().(*logfmtEncoder) - enc.Reset() - defer logfmtEncoderPool.Put(enc) - - if err := enc.EncodeKeyvals(keyvals...); err != nil { - return err - } - - // Add newline to the end of the buffer - if err := enc.EndRecord(); err != nil { - return err - } - - // The Logger interface requires implementations to be safe for concurrent - // use by multiple goroutines. For this implementation that means making - // only one call to l.w.Write() for each call to Log. - if _, err := l.w.Write(enc.buf.Bytes()); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/go-kit/log/nop_logger.go b/vendor/github.com/go-kit/log/nop_logger.go deleted file mode 100644 index 1047d626..00000000 --- a/vendor/github.com/go-kit/log/nop_logger.go +++ /dev/null @@ -1,8 +0,0 @@ -package log - -type nopLogger struct{} - -// NewNopLogger returns a logger that doesn't do anything. -func NewNopLogger() Logger { return nopLogger{} } - -func (nopLogger) Log(...interface{}) error { return nil } diff --git a/vendor/github.com/go-kit/log/staticcheck.conf b/vendor/github.com/go-kit/log/staticcheck.conf deleted file mode 100644 index 528438b9..00000000 --- a/vendor/github.com/go-kit/log/staticcheck.conf +++ /dev/null @@ -1 +0,0 @@ -checks = ["all"] diff --git a/vendor/github.com/go-kit/log/stdlib.go b/vendor/github.com/go-kit/log/stdlib.go deleted file mode 100644 index 0338edbe..00000000 --- a/vendor/github.com/go-kit/log/stdlib.go +++ /dev/null @@ -1,151 +0,0 @@ -package log - -import ( - "bytes" - "io" - "log" - "regexp" - "strings" -) - -// StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's -// designed to be passed to a Go kit logger as the writer, for cases where -// it's necessary to redirect all Go kit log output to the stdlib logger. -// -// If you have any choice in the matter, you shouldn't use this. Prefer to -// redirect the stdlib log to the Go kit logger via NewStdlibAdapter. -type StdlibWriter struct{} - -// Write implements io.Writer. -func (w StdlibWriter) Write(p []byte) (int, error) { - log.Print(strings.TrimSpace(string(p))) - return len(p), nil -} - -// StdlibAdapter wraps a Logger and allows it to be passed to the stdlib -// logger's SetOutput. It will extract date/timestamps, filenames, and -// messages, and place them under relevant keys. -type StdlibAdapter struct { - Logger - timestampKey string - fileKey string - messageKey string - prefix string - joinPrefixToMsg bool -} - -// StdlibAdapterOption sets a parameter for the StdlibAdapter. -type StdlibAdapterOption func(*StdlibAdapter) - -// TimestampKey sets the key for the timestamp field. By default, it's "ts". -func TimestampKey(key string) StdlibAdapterOption { - return func(a *StdlibAdapter) { a.timestampKey = key } -} - -// FileKey sets the key for the file and line field. By default, it's "caller". -func FileKey(key string) StdlibAdapterOption { - return func(a *StdlibAdapter) { a.fileKey = key } -} - -// MessageKey sets the key for the actual log message. By default, it's "msg". -func MessageKey(key string) StdlibAdapterOption { - return func(a *StdlibAdapter) { a.messageKey = key } -} - -// Prefix configures the adapter to parse a prefix from stdlib log events. If -// you provide a non-empty prefix to the stdlib logger, then your should provide -// that same prefix to the adapter via this option. -// -// By default, the prefix isn't included in the msg key. Set joinPrefixToMsg to -// true if you want to include the parsed prefix in the msg. -func Prefix(prefix string, joinPrefixToMsg bool) StdlibAdapterOption { - return func(a *StdlibAdapter) { a.prefix = prefix; a.joinPrefixToMsg = joinPrefixToMsg } -} - -// NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed -// logger. It's designed to be passed to log.SetOutput. -func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer { - a := StdlibAdapter{ - Logger: logger, - timestampKey: "ts", - fileKey: "caller", - messageKey: "msg", - } - for _, option := range options { - option(&a) - } - return a -} - -func (a StdlibAdapter) Write(p []byte) (int, error) { - p = a.handlePrefix(p) - - result := subexps(p) - keyvals := []interface{}{} - var timestamp string - if date, ok := result["date"]; ok && date != "" { - timestamp = date - } - if time, ok := result["time"]; ok && time != "" { - if timestamp != "" { - timestamp += " " - } - timestamp += time - } - if timestamp != "" { - keyvals = append(keyvals, a.timestampKey, timestamp) - } - if file, ok := result["file"]; ok && file != "" { - keyvals = append(keyvals, a.fileKey, file) - } - if msg, ok := result["msg"]; ok { - msg = a.handleMessagePrefix(msg) - keyvals = append(keyvals, a.messageKey, msg) - } - if err := a.Logger.Log(keyvals...); err != nil { - return 0, err - } - return len(p), nil -} - -func (a StdlibAdapter) handlePrefix(p []byte) []byte { - if a.prefix != "" { - p = bytes.TrimPrefix(p, []byte(a.prefix)) - } - return p -} - -func (a StdlibAdapter) handleMessagePrefix(msg string) string { - if a.prefix == "" { - return msg - } - - msg = strings.TrimPrefix(msg, a.prefix) - if a.joinPrefixToMsg { - msg = a.prefix + msg - } - return msg -} - -const ( - logRegexpDate = `(?P<date>[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?` - logRegexpTime = `(?P<time>[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?)?[ ]?` - logRegexpFile = `(?P<file>.+?:[0-9]+)?` - logRegexpMsg = `(: )?(?P<msg>(?s:.*))` -) - -var ( - logRegexp = regexp.MustCompile(logRegexpDate + logRegexpTime + logRegexpFile + logRegexpMsg) -) - -func subexps(line []byte) map[string]string { - m := logRegexp.FindSubmatch(line) - if len(m) < len(logRegexp.SubexpNames()) { - return map[string]string{} - } - result := map[string]string{} - for i, name := range logRegexp.SubexpNames() { - result[name] = strings.TrimRight(string(m[i]), "\n") - } - return result -} diff --git a/vendor/github.com/go-kit/log/sync.go b/vendor/github.com/go-kit/log/sync.go deleted file mode 100644 index 58bd3add..00000000 --- a/vendor/github.com/go-kit/log/sync.go +++ /dev/null @@ -1,113 +0,0 @@ -package log - -import ( - "io" - "sync" - "sync/atomic" -) - -// SwapLogger wraps another logger that may be safely replaced while other -// goroutines use the SwapLogger concurrently. The zero value for a SwapLogger -// will discard all log events without error. -// -// SwapLogger serves well as a package global logger that can be changed by -// importers. -type SwapLogger struct { - logger atomic.Value -} - -type loggerStruct struct { - Logger -} - -// Log implements the Logger interface by forwarding keyvals to the currently -// wrapped logger. It does not log anything if the wrapped logger is nil. -func (l *SwapLogger) Log(keyvals ...interface{}) error { - s, ok := l.logger.Load().(loggerStruct) - if !ok || s.Logger == nil { - return nil - } - return s.Log(keyvals...) -} - -// Swap replaces the currently wrapped logger with logger. Swap may be called -// concurrently with calls to Log from other goroutines. -func (l *SwapLogger) Swap(logger Logger) { - l.logger.Store(loggerStruct{logger}) -} - -// NewSyncWriter returns a new writer that is safe for concurrent use by -// multiple goroutines. Writes to the returned writer are passed on to w. If -// another write is already in progress, the calling goroutine blocks until -// the writer is available. -// -// If w implements the following interface, so does the returned writer. -// -// interface { -// Fd() uintptr -// } -func NewSyncWriter(w io.Writer) io.Writer { - switch w := w.(type) { - case fdWriter: - return &fdSyncWriter{fdWriter: w} - default: - return &syncWriter{Writer: w} - } -} - -// syncWriter synchronizes concurrent writes to an io.Writer. -type syncWriter struct { - sync.Mutex - io.Writer -} - -// Write writes p to the underlying io.Writer. If another write is already in -// progress, the calling goroutine blocks until the syncWriter is available. -func (w *syncWriter) Write(p []byte) (n int, err error) { - w.Lock() - defer w.Unlock() - return w.Writer.Write(p) -} - -// fdWriter is an io.Writer that also has an Fd method. The most common -// example of an fdWriter is an *os.File. -type fdWriter interface { - io.Writer - Fd() uintptr -} - -// fdSyncWriter synchronizes concurrent writes to an fdWriter. -type fdSyncWriter struct { - sync.Mutex - fdWriter -} - -// Write writes p to the underlying io.Writer. If another write is already in -// progress, the calling goroutine blocks until the fdSyncWriter is available. -func (w *fdSyncWriter) Write(p []byte) (n int, err error) { - w.Lock() - defer w.Unlock() - return w.fdWriter.Write(p) -} - -// syncLogger provides concurrent safe logging for another Logger. -type syncLogger struct { - mu sync.Mutex - logger Logger -} - -// NewSyncLogger returns a logger that synchronizes concurrent use of the -// wrapped logger. When multiple goroutines use the SyncLogger concurrently -// only one goroutine will be allowed to log to the wrapped logger at a time. -// The other goroutines will block until the logger is available. -func NewSyncLogger(logger Logger) Logger { - return &syncLogger{logger: logger} -} - -// Log logs keyvals to the underlying Logger. If another log is already in -// progress, the calling goroutine blocks until the syncLogger is available. -func (l *syncLogger) Log(keyvals ...interface{}) error { - l.mu.Lock() - defer l.mu.Unlock() - return l.logger.Log(keyvals...) -} diff --git a/vendor/github.com/go-kit/log/value.go b/vendor/github.com/go-kit/log/value.go deleted file mode 100644 index 3ce197f7..00000000 --- a/vendor/github.com/go-kit/log/value.go +++ /dev/null @@ -1,110 +0,0 @@ -package log - -import ( - "runtime" - "strconv" - "strings" - "time" -) - -// A Valuer generates a log value. When passed to With, WithPrefix, or -// WithSuffix in a value element (odd indexes), it represents a dynamic -// value which is re-evaluated with each log event. -type Valuer func() interface{} - -// bindValues replaces all value elements (odd indexes) containing a Valuer -// with their generated value. -func bindValues(keyvals []interface{}) { - for i := 1; i < len(keyvals); i += 2 { - if v, ok := keyvals[i].(Valuer); ok { - keyvals[i] = v() - } - } -} - -// containsValuer returns true if any of the value elements (odd indexes) -// contain a Valuer. -func containsValuer(keyvals []interface{}) bool { - for i := 1; i < len(keyvals); i += 2 { - if _, ok := keyvals[i].(Valuer); ok { - return true - } - } - return false -} - -// Timestamp returns a timestamp Valuer. It invokes the t function to get the -// time; unless you are doing something tricky, pass time.Now. -// -// Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which -// are TimestampFormats that use the RFC3339Nano format. -func Timestamp(t func() time.Time) Valuer { - return func() interface{} { return t() } -} - -// TimestampFormat returns a timestamp Valuer with a custom time format. It -// invokes the t function to get the time to format; unless you are doing -// something tricky, pass time.Now. The layout string is passed to -// Time.Format. -// -// Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which -// are TimestampFormats that use the RFC3339Nano format. -func TimestampFormat(t func() time.Time, layout string) Valuer { - return func() interface{} { - return timeFormat{ - time: t(), - layout: layout, - } - } -} - -// A timeFormat represents an instant in time and a layout used when -// marshaling to a text format. -type timeFormat struct { - time time.Time - layout string -} - -func (tf timeFormat) String() string { - return tf.time.Format(tf.layout) -} - -// MarshalText implements encoding.TextMarshaller. -func (tf timeFormat) MarshalText() (text []byte, err error) { - // The following code adapted from the standard library time.Time.Format - // method. Using the same undocumented magic constant to extend the size - // of the buffer as seen there. - b := make([]byte, 0, len(tf.layout)+10) - b = tf.time.AppendFormat(b, tf.layout) - return b, nil -} - -// Caller returns a Valuer that returns a file and line from a specified depth -// in the callstack. Users will probably want to use DefaultCaller. -func Caller(depth int) Valuer { - return func() interface{} { - _, file, line, _ := runtime.Caller(depth) - idx := strings.LastIndexByte(file, '/') - // using idx+1 below handles both of following cases: - // idx == -1 because no "/" was found, or - // idx >= 0 and we want to start at the character after the found "/". - return file[idx+1:] + ":" + strconv.Itoa(line) - } -} - -var ( - // DefaultTimestamp is a Valuer that returns the current wallclock time, - // respecting time zones, when bound. - DefaultTimestamp = TimestampFormat(time.Now, time.RFC3339Nano) - - // DefaultTimestampUTC is a Valuer that returns the current time in UTC - // when bound. - DefaultTimestampUTC = TimestampFormat( - func() time.Time { return time.Now().UTC() }, - time.RFC3339Nano, - ) - - // DefaultCaller is a Valuer that returns the file and line where the Log - // method was invoked. It can only be used with log.With. - DefaultCaller = Caller(3) -) diff --git a/vendor/github.com/go-logfmt/logfmt/.gitignore b/vendor/github.com/go-logfmt/logfmt/.gitignore deleted file mode 100644 index 1d74e219..00000000 --- a/vendor/github.com/go-logfmt/logfmt/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.vscode/ diff --git a/vendor/github.com/go-logfmt/logfmt/CHANGELOG.md b/vendor/github.com/go-logfmt/logfmt/CHANGELOG.md deleted file mode 100644 index 1a9a27bc..00000000 --- a/vendor/github.com/go-logfmt/logfmt/CHANGELOG.md +++ /dev/null @@ -1,48 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [0.5.0] - 2020-01-03 - -### Changed -- Remove the dependency on github.com/kr/logfmt by [@ChrisHines] -- Move fuzz code to github.com/go-logfmt/fuzzlogfmt by [@ChrisHines] - -## [0.4.0] - 2018-11-21 - -### Added -- Go module support by [@ChrisHines] -- CHANGELOG by [@ChrisHines] - -### Changed -- Drop invalid runes from keys instead of returning ErrInvalidKey by [@ChrisHines] -- On panic while printing, attempt to print panic value by [@bboreham] - -## [0.3.0] - 2016-11-15 -### Added -- Pool buffers for quoted strings and byte slices by [@nussjustin] -### Fixed -- Fuzz fix, quote invalid UTF-8 values by [@judwhite] - -## [0.2.0] - 2016-05-08 -### Added -- Encoder.EncodeKeyvals by [@ChrisHines] - -## [0.1.0] - 2016-03-28 -### Added -- Encoder by [@ChrisHines] -- Decoder by [@ChrisHines] -- MarshalKeyvals by [@ChrisHines] - -[0.5.0]: https://github.com/go-logfmt/logfmt/compare/v0.4.0...v0.5.0 -[0.4.0]: https://github.com/go-logfmt/logfmt/compare/v0.3.0...v0.4.0 -[0.3.0]: https://github.com/go-logfmt/logfmt/compare/v0.2.0...v0.3.0 -[0.2.0]: https://github.com/go-logfmt/logfmt/compare/v0.1.0...v0.2.0 -[0.1.0]: https://github.com/go-logfmt/logfmt/commits/v0.1.0 - -[@ChrisHines]: https://github.com/ChrisHines -[@bboreham]: https://github.com/bboreham -[@judwhite]: https://github.com/judwhite -[@nussjustin]: https://github.com/nussjustin diff --git a/vendor/github.com/go-logfmt/logfmt/LICENSE b/vendor/github.com/go-logfmt/logfmt/LICENSE deleted file mode 100644 index c0265089..00000000 --- a/vendor/github.com/go-logfmt/logfmt/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 go-logfmt - -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/go-logfmt/logfmt/README.md b/vendor/github.com/go-logfmt/logfmt/README.md deleted file mode 100644 index 8e48fcd3..00000000 --- a/vendor/github.com/go-logfmt/logfmt/README.md +++ /dev/null @@ -1,33 +0,0 @@ -[![Go Reference](https://pkg.go.dev/badge/github.com/go-logfmt/logfmt.svg)](https://pkg.go.dev/github.com/go-logfmt/logfmt) -[![Go Report Card](https://goreportcard.com/badge/go-logfmt/logfmt)](https://goreportcard.com/report/go-logfmt/logfmt) -[![Github Actions](https://github.com/go-logfmt/logfmt/actions/workflows/test.yml/badge.svg)](https://github.com/go-logfmt/logfmt/actions/workflows/test.yml) -[![Coverage Status](https://coveralls.io/repos/github/go-logfmt/logfmt/badge.svg?branch=master)](https://coveralls.io/github/go-logfmt/logfmt?branch=master) - -# logfmt - -Package logfmt implements utilities to marshal and unmarshal data in the [logfmt -format](https://brandur.org/logfmt). It provides an API similar to -[encoding/json](http://golang.org/pkg/encoding/json/) and -[encoding/xml](http://golang.org/pkg/encoding/xml/). - -The logfmt format was first documented by Brandur Leach in [this -article](https://brandur.org/logfmt). The format has not been formally -standardized. The most authoritative public specification to date has been the -documentation of a Go Language [package](http://godoc.org/github.com/kr/logfmt) -written by Blake Mizerany and Keith Rarick. - -## Goals - -This project attempts to conform as closely as possible to the prior art, while -also removing ambiguity where necessary to provide well behaved encoder and -decoder implementations. - -## Non-goals - -This project does not attempt to formally standardize the logfmt format. In the -event that logfmt is standardized this project would take conforming to the -standard as a goal. - -## Versioning - -Package logfmt publishes releases via [semver](http://semver.org/) compatible Git tags prefixed with a single 'v'. diff --git a/vendor/github.com/go-logfmt/logfmt/decode.go b/vendor/github.com/go-logfmt/logfmt/decode.go deleted file mode 100644 index 2013708e..00000000 --- a/vendor/github.com/go-logfmt/logfmt/decode.go +++ /dev/null @@ -1,237 +0,0 @@ -package logfmt - -import ( - "bufio" - "bytes" - "fmt" - "io" - "unicode/utf8" -) - -// A Decoder reads and decodes logfmt records from an input stream. -type Decoder struct { - pos int - key []byte - value []byte - lineNum int - s *bufio.Scanner - err error -} - -// NewDecoder returns a new decoder that reads from r. -// -// The decoder introduces its own buffering and may read data from r beyond -// the logfmt records requested. -func NewDecoder(r io.Reader) *Decoder { - dec := &Decoder{ - s: bufio.NewScanner(r), - } - return dec -} - -// ScanRecord advances the Decoder to the next record, which can then be -// parsed with the ScanKeyval method. It returns false when decoding stops, -// either by reaching the end of the input or an error. After ScanRecord -// returns false, the Err method will return any error that occurred during -// decoding, except that if it was io.EOF, Err will return nil. -func (dec *Decoder) ScanRecord() bool { - if dec.err != nil { - return false - } - if !dec.s.Scan() { - dec.err = dec.s.Err() - return false - } - dec.lineNum++ - dec.pos = 0 - return true -} - -// ScanKeyval advances the Decoder to the next key/value pair of the current -// record, which can then be retrieved with the Key and Value methods. It -// returns false when decoding stops, either by reaching the end of the -// current record or an error. -func (dec *Decoder) ScanKeyval() bool { - dec.key, dec.value = nil, nil - if dec.err != nil { - return false - } - - line := dec.s.Bytes() - - // garbage - for p, c := range line[dec.pos:] { - if c > ' ' { - dec.pos += p - goto key - } - } - dec.pos = len(line) - return false - -key: - const invalidKeyError = "invalid key" - - start, multibyte := dec.pos, false - for p, c := range line[dec.pos:] { - switch { - case c == '=': - dec.pos += p - if dec.pos > start { - dec.key = line[start:dec.pos] - if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) { - dec.syntaxError(invalidKeyError) - return false - } - } - if dec.key == nil { - dec.unexpectedByte(c) - return false - } - goto equal - case c == '"': - dec.pos += p - dec.unexpectedByte(c) - return false - case c <= ' ': - dec.pos += p - if dec.pos > start { - dec.key = line[start:dec.pos] - if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) { - dec.syntaxError(invalidKeyError) - return false - } - } - return true - case c >= utf8.RuneSelf: - multibyte = true - } - } - dec.pos = len(line) - if dec.pos > start { - dec.key = line[start:dec.pos] - if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) { - dec.syntaxError(invalidKeyError) - return false - } - } - return true - -equal: - dec.pos++ - if dec.pos >= len(line) { - return true - } - switch c := line[dec.pos]; { - case c <= ' ': - return true - case c == '"': - goto qvalue - } - - // value - start = dec.pos - for p, c := range line[dec.pos:] { - switch { - case c == '=' || c == '"': - dec.pos += p - dec.unexpectedByte(c) - return false - case c <= ' ': - dec.pos += p - if dec.pos > start { - dec.value = line[start:dec.pos] - } - return true - } - } - dec.pos = len(line) - if dec.pos > start { - dec.value = line[start:dec.pos] - } - return true - -qvalue: - const ( - untermQuote = "unterminated quoted value" - invalidQuote = "invalid quoted value" - ) - - hasEsc, esc := false, false - start = dec.pos - for p, c := range line[dec.pos+1:] { - switch { - case esc: - esc = false - case c == '\\': - hasEsc, esc = true, true - case c == '"': - dec.pos += p + 2 - if hasEsc { - v, ok := unquoteBytes(line[start:dec.pos]) - if !ok { - dec.syntaxError(invalidQuote) - return false - } - dec.value = v - } else { - start++ - end := dec.pos - 1 - if end > start { - dec.value = line[start:end] - } - } - return true - } - } - dec.pos = len(line) - dec.syntaxError(untermQuote) - return false -} - -// Key returns the most recent key found by a call to ScanKeyval. The returned -// slice may point to internal buffers and is only valid until the next call -// to ScanRecord. It does no allocation. -func (dec *Decoder) Key() []byte { - return dec.key -} - -// Value returns the most recent value found by a call to ScanKeyval. The -// returned slice may point to internal buffers and is only valid until the -// next call to ScanRecord. It does no allocation when the value has no -// escape sequences. -func (dec *Decoder) Value() []byte { - return dec.value -} - -// Err returns the first non-EOF error that was encountered by the Scanner. -func (dec *Decoder) Err() error { - return dec.err -} - -func (dec *Decoder) syntaxError(msg string) { - dec.err = &SyntaxError{ - Msg: msg, - Line: dec.lineNum, - Pos: dec.pos + 1, - } -} - -func (dec *Decoder) unexpectedByte(c byte) { - dec.err = &SyntaxError{ - Msg: fmt.Sprintf("unexpected %q", c), - Line: dec.lineNum, - Pos: dec.pos + 1, - } -} - -// A SyntaxError represents a syntax error in the logfmt input stream. -type SyntaxError struct { - Msg string - Line int - Pos int -} - -func (e *SyntaxError) Error() string { - return fmt.Sprintf("logfmt syntax error at pos %d on line %d: %s", e.Pos, e.Line, e.Msg) -} diff --git a/vendor/github.com/go-logfmt/logfmt/doc.go b/vendor/github.com/go-logfmt/logfmt/doc.go deleted file mode 100644 index 378e9ad1..00000000 --- a/vendor/github.com/go-logfmt/logfmt/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Package logfmt implements utilities to marshal and unmarshal data in the -// logfmt format. The logfmt format records key/value pairs in a way that -// balances readability for humans and simplicity of computer parsing. It is -// most commonly used as a more human friendly alternative to JSON for -// structured logging. -package logfmt diff --git a/vendor/github.com/go-logfmt/logfmt/encode.go b/vendor/github.com/go-logfmt/logfmt/encode.go deleted file mode 100644 index 4ea9d239..00000000 --- a/vendor/github.com/go-logfmt/logfmt/encode.go +++ /dev/null @@ -1,322 +0,0 @@ -package logfmt - -import ( - "bytes" - "encoding" - "errors" - "fmt" - "io" - "reflect" - "strings" - "unicode/utf8" -) - -// MarshalKeyvals returns the logfmt encoding of keyvals, a variadic sequence -// of alternating keys and values. -func MarshalKeyvals(keyvals ...interface{}) ([]byte, error) { - buf := &bytes.Buffer{} - if err := NewEncoder(buf).EncodeKeyvals(keyvals...); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// An Encoder writes logfmt data to an output stream. -type Encoder struct { - w io.Writer - scratch bytes.Buffer - needSep bool -} - -// NewEncoder returns a new encoder that writes to w. -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{ - w: w, - } -} - -var ( - space = []byte(" ") - equals = []byte("=") - newline = []byte("\n") - null = []byte("null") -) - -// EncodeKeyval writes the logfmt encoding of key and value to the stream. A -// single space is written before the second and subsequent keys in a record. -// Nothing is written if a non-nil error is returned. -func (enc *Encoder) EncodeKeyval(key, value interface{}) error { - enc.scratch.Reset() - if enc.needSep { - if _, err := enc.scratch.Write(space); err != nil { - return err - } - } - if err := writeKey(&enc.scratch, key); err != nil { - return err - } - if _, err := enc.scratch.Write(equals); err != nil { - return err - } - if err := writeValue(&enc.scratch, value); err != nil { - return err - } - _, err := enc.w.Write(enc.scratch.Bytes()) - enc.needSep = true - return err -} - -// EncodeKeyvals writes the logfmt encoding of keyvals to the stream. Keyvals -// is a variadic sequence of alternating keys and values. Keys of unsupported -// type are skipped along with their corresponding value. Values of -// unsupported type or that cause a MarshalerError are replaced by their error -// but do not cause EncodeKeyvals to return an error. If a non-nil error is -// returned some key/value pairs may not have be written. -func (enc *Encoder) EncodeKeyvals(keyvals ...interface{}) error { - if len(keyvals) == 0 { - return nil - } - if len(keyvals)%2 == 1 { - keyvals = append(keyvals, nil) - } - for i := 0; i < len(keyvals); i += 2 { - k, v := keyvals[i], keyvals[i+1] - err := enc.EncodeKeyval(k, v) - if err == ErrUnsupportedKeyType { - continue - } - if _, ok := err.(*MarshalerError); ok || err == ErrUnsupportedValueType { - v = err - err = enc.EncodeKeyval(k, v) - } - if err != nil { - return err - } - } - return nil -} - -// MarshalerError represents an error encountered while marshaling a value. -type MarshalerError struct { - Type reflect.Type - Err error -} - -func (e *MarshalerError) Error() string { - return "error marshaling value of type " + e.Type.String() + ": " + e.Err.Error() -} - -// ErrNilKey is returned by Marshal functions and Encoder methods if a key is -// a nil interface or pointer value. -var ErrNilKey = errors.New("nil key") - -// ErrInvalidKey is returned by Marshal functions and Encoder methods if, after -// dropping invalid runes, a key is empty. -var ErrInvalidKey = errors.New("invalid key") - -// ErrUnsupportedKeyType is returned by Encoder methods if a key has an -// unsupported type. -var ErrUnsupportedKeyType = errors.New("unsupported key type") - -// ErrUnsupportedValueType is returned by Encoder methods if a value has an -// unsupported type. -var ErrUnsupportedValueType = errors.New("unsupported value type") - -func writeKey(w io.Writer, key interface{}) error { - if key == nil { - return ErrNilKey - } - - switch k := key.(type) { - case string: - return writeStringKey(w, k) - case []byte: - if k == nil { - return ErrNilKey - } - return writeBytesKey(w, k) - case encoding.TextMarshaler: - kb, err := safeMarshal(k) - if err != nil { - return err - } - if kb == nil { - return ErrNilKey - } - return writeBytesKey(w, kb) - case fmt.Stringer: - ks, ok := safeString(k) - if !ok { - return ErrNilKey - } - return writeStringKey(w, ks) - default: - rkey := reflect.ValueOf(key) - switch rkey.Kind() { - case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Struct: - return ErrUnsupportedKeyType - case reflect.Ptr: - if rkey.IsNil() { - return ErrNilKey - } - return writeKey(w, rkey.Elem().Interface()) - } - return writeStringKey(w, fmt.Sprint(k)) - } -} - -// keyRuneFilter returns r for all valid key runes, and -1 for all invalid key -// runes. When used as the mapping function for strings.Map and bytes.Map -// functions it causes them to remove invalid key runes from strings or byte -// slices respectively. -func keyRuneFilter(r rune) rune { - if r <= ' ' || r == '=' || r == '"' || r == utf8.RuneError { - return -1 - } - return r -} - -func writeStringKey(w io.Writer, key string) error { - k := strings.Map(keyRuneFilter, key) - if k == "" { - return ErrInvalidKey - } - _, err := io.WriteString(w, k) - return err -} - -func writeBytesKey(w io.Writer, key []byte) error { - k := bytes.Map(keyRuneFilter, key) - if len(k) == 0 { - return ErrInvalidKey - } - _, err := w.Write(k) - return err -} - -func writeValue(w io.Writer, value interface{}) error { - switch v := value.(type) { - case nil: - return writeBytesValue(w, null) - case string: - return writeStringValue(w, v, true) - case []byte: - return writeBytesValue(w, v) - case encoding.TextMarshaler: - vb, err := safeMarshal(v) - if err != nil { - return err - } - if vb == nil { - vb = null - } - return writeBytesValue(w, vb) - case error: - se, ok := safeError(v) - return writeStringValue(w, se, ok) - case fmt.Stringer: - ss, ok := safeString(v) - return writeStringValue(w, ss, ok) - default: - rvalue := reflect.ValueOf(value) - switch rvalue.Kind() { - case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Struct: - return ErrUnsupportedValueType - case reflect.Ptr: - if rvalue.IsNil() { - return writeBytesValue(w, null) - } - return writeValue(w, rvalue.Elem().Interface()) - } - return writeStringValue(w, fmt.Sprint(v), true) - } -} - -func needsQuotedValueRune(r rune) bool { - return r <= ' ' || r == '=' || r == '"' || r == utf8.RuneError -} - -func writeStringValue(w io.Writer, value string, ok bool) error { - var err error - if ok && value == "null" { - _, err = io.WriteString(w, `"null"`) - } else if strings.IndexFunc(value, needsQuotedValueRune) != -1 { - _, err = writeQuotedString(w, value) - } else { - _, err = io.WriteString(w, value) - } - return err -} - -func writeBytesValue(w io.Writer, value []byte) error { - var err error - if bytes.IndexFunc(value, needsQuotedValueRune) != -1 { - _, err = writeQuotedBytes(w, value) - } else { - _, err = w.Write(value) - } - return err -} - -// EndRecord writes a newline character to the stream and resets the encoder -// to the beginning of a new record. -func (enc *Encoder) EndRecord() error { - _, err := enc.w.Write(newline) - if err == nil { - enc.needSep = false - } - return err -} - -// Reset resets the encoder to the beginning of a new record. -func (enc *Encoder) Reset() { - enc.needSep = false -} - -func safeError(err error) (s string, ok bool) { - defer func() { - if panicVal := recover(); panicVal != nil { - if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { - s, ok = "null", false - } else { - s, ok = fmt.Sprintf("PANIC:%v", panicVal), false - } - } - }() - s, ok = err.Error(), true - return -} - -func safeString(str fmt.Stringer) (s string, ok bool) { - defer func() { - if panicVal := recover(); panicVal != nil { - if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() { - s, ok = "null", false - } else { - s, ok = fmt.Sprintf("PANIC:%v", panicVal), true - } - } - }() - s, ok = str.String(), true - return -} - -func safeMarshal(tm encoding.TextMarshaler) (b []byte, err error) { - defer func() { - if panicVal := recover(); panicVal != nil { - if v := reflect.ValueOf(tm); v.Kind() == reflect.Ptr && v.IsNil() { - b, err = nil, nil - } else { - b, err = nil, fmt.Errorf("panic when marshalling: %s", panicVal) - } - } - }() - b, err = tm.MarshalText() - if err != nil { - return nil, &MarshalerError{ - Type: reflect.TypeOf(tm), - Err: err, - } - } - return -} diff --git a/vendor/github.com/go-logfmt/logfmt/jsonstring.go b/vendor/github.com/go-logfmt/logfmt/jsonstring.go deleted file mode 100644 index 030ac85f..00000000 --- a/vendor/github.com/go-logfmt/logfmt/jsonstring.go +++ /dev/null @@ -1,277 +0,0 @@ -package logfmt - -import ( - "bytes" - "io" - "strconv" - "sync" - "unicode" - "unicode/utf16" - "unicode/utf8" -) - -// Taken from Go's encoding/json and modified for use here. - -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -var hex = "0123456789abcdef" - -var bufferPool = sync.Pool{ - New: func() interface{} { - return &bytes.Buffer{} - }, -} - -func getBuffer() *bytes.Buffer { - return bufferPool.Get().(*bytes.Buffer) -} - -func poolBuffer(buf *bytes.Buffer) { - buf.Reset() - bufferPool.Put(buf) -} - -// NOTE: keep in sync with writeQuotedBytes below. -func writeQuotedString(w io.Writer, s string) (int, error) { - buf := getBuffer() - buf.WriteByte('"') - start := 0 - for i := 0; i < len(s); { - if b := s[i]; b < utf8.RuneSelf { - if 0x20 <= b && b != '\\' && b != '"' { - i++ - continue - } - if start < i { - buf.WriteString(s[start:i]) - } - switch b { - case '\\', '"': - buf.WriteByte('\\') - buf.WriteByte(b) - case '\n': - buf.WriteByte('\\') - buf.WriteByte('n') - case '\r': - buf.WriteByte('\\') - buf.WriteByte('r') - case '\t': - buf.WriteByte('\\') - buf.WriteByte('t') - default: - // This encodes bytes < 0x20 except for \n, \r, and \t. - buf.WriteString(`\u00`) - buf.WriteByte(hex[b>>4]) - buf.WriteByte(hex[b&0xF]) - } - i++ - start = i - continue - } - c, size := utf8.DecodeRuneInString(s[i:]) - if c == utf8.RuneError { - if start < i { - buf.WriteString(s[start:i]) - } - buf.WriteString(`\ufffd`) - i += size - start = i - continue - } - i += size - } - if start < len(s) { - buf.WriteString(s[start:]) - } - buf.WriteByte('"') - n, err := w.Write(buf.Bytes()) - poolBuffer(buf) - return n, err -} - -// NOTE: keep in sync with writeQuoteString above. -func writeQuotedBytes(w io.Writer, s []byte) (int, error) { - buf := getBuffer() - buf.WriteByte('"') - start := 0 - for i := 0; i < len(s); { - if b := s[i]; b < utf8.RuneSelf { - if 0x20 <= b && b != '\\' && b != '"' { - i++ - continue - } - if start < i { - buf.Write(s[start:i]) - } - switch b { - case '\\', '"': - buf.WriteByte('\\') - buf.WriteByte(b) - case '\n': - buf.WriteByte('\\') - buf.WriteByte('n') - case '\r': - buf.WriteByte('\\') - buf.WriteByte('r') - case '\t': - buf.WriteByte('\\') - buf.WriteByte('t') - default: - // This encodes bytes < 0x20 except for \n, \r, and \t. - buf.WriteString(`\u00`) - buf.WriteByte(hex[b>>4]) - buf.WriteByte(hex[b&0xF]) - } - i++ - start = i - continue - } - c, size := utf8.DecodeRune(s[i:]) - if c == utf8.RuneError { - if start < i { - buf.Write(s[start:i]) - } - buf.WriteString(`\ufffd`) - i += size - start = i - continue - } - i += size - } - if start < len(s) { - buf.Write(s[start:]) - } - buf.WriteByte('"') - n, err := w.Write(buf.Bytes()) - poolBuffer(buf) - return n, err -} - -// getu4 decodes \uXXXX from the beginning of s, returning the hex value, -// or it returns -1. -func getu4(s []byte) rune { - if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { - return -1 - } - r, err := strconv.ParseUint(string(s[2:6]), 16, 64) - if err != nil { - return -1 - } - return rune(r) -} - -func unquoteBytes(s []byte) (t []byte, ok bool) { - if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { - return - } - s = s[1 : len(s)-1] - - // Check for unusual characters. If there are none, - // then no unquoting is needed, so return a slice of the - // original bytes. - r := 0 - for r < len(s) { - c := s[r] - if c == '\\' || c == '"' || c < ' ' { - break - } - if c < utf8.RuneSelf { - r++ - continue - } - rr, size := utf8.DecodeRune(s[r:]) - if rr == utf8.RuneError { - break - } - r += size - } - if r == len(s) { - return s, true - } - - b := make([]byte, len(s)+2*utf8.UTFMax) - w := copy(b, s[0:r]) - for r < len(s) { - // Out of room? Can only happen if s is full of - // malformed UTF-8 and we're replacing each - // byte with RuneError. - if w >= len(b)-2*utf8.UTFMax { - nb := make([]byte, (len(b)+utf8.UTFMax)*2) - copy(nb, b[0:w]) - b = nb - } - switch c := s[r]; { - case c == '\\': - r++ - if r >= len(s) { - return - } - switch s[r] { - default: - return - case '"', '\\', '/', '\'': - b[w] = s[r] - r++ - w++ - case 'b': - b[w] = '\b' - r++ - w++ - case 'f': - b[w] = '\f' - r++ - w++ - case 'n': - b[w] = '\n' - r++ - w++ - case 'r': - b[w] = '\r' - r++ - w++ - case 't': - b[w] = '\t' - r++ - w++ - case 'u': - r-- - rr := getu4(s[r:]) - if rr < 0 { - return - } - r += 6 - if utf16.IsSurrogate(rr) { - rr1 := getu4(s[r:]) - if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { - // A valid pair; consume. - r += 6 - w += utf8.EncodeRune(b[w:], dec) - break - } - // Invalid surrogate; fall back to replacement rune. - rr = unicode.ReplacementChar - } - w += utf8.EncodeRune(b[w:], rr) - } - - // Quote, control characters are invalid. - case c == '"', c < ' ': - return - - // ASCII - case c < utf8.RuneSelf: - b[w] = c - r++ - w++ - - // Coerce to well-formed UTF-8. - default: - rr, size := utf8.DecodeRune(s[r:]) - r += size - w += utf8.EncodeRune(b[w:], rr) - } - } - return b[0:w], true -} diff --git a/vendor/github.com/prometheus/common/promlog/log.go b/vendor/github.com/prometheus/common/promlog/log.go deleted file mode 100644 index 24a11622..00000000 --- a/vendor/github.com/prometheus/common/promlog/log.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2017 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package promlog defines standardised ways to initialize Go kit loggers -// across Prometheus components. -// It should typically only ever be imported by main packages. -// -// Deprecated: This package has been deprecated in favor of migrating to -// `github.com/prometheus/common/promslog` which uses the Go standard library -// `log/slog` package. -package promlog - -import ( - "fmt" - "os" - "sync" - "time" - - "github.com/go-kit/log" - "github.com/go-kit/log/level" -) - -var ( - // This timestamp format differs from RFC3339Nano by using .000 instead - // of .999999999 which changes the timestamp from 9 variable to 3 fixed - // decimals (.130 instead of .130987456). - timestampFormat = log.TimestampFormat( - func() time.Time { return time.Now().UTC() }, - "2006-01-02T15:04:05.000Z07:00", - ) - - LevelFlagOptions = []string{"debug", "info", "warn", "error"} - FormatFlagOptions = []string{"logfmt", "json"} -) - -// AllowedLevel is a settable identifier for the minimum level a log entry -// must be have. -type AllowedLevel struct { - s string - o level.Option -} - -func (l *AllowedLevel) UnmarshalYAML(unmarshal func(interface{}) error) error { - var s string - type plain string - if err := unmarshal((*plain)(&s)); err != nil { - return err - } - if s == "" { - return nil - } - lo := &AllowedLevel{} - if err := lo.Set(s); err != nil { - return err - } - *l = *lo - return nil -} - -func (l *AllowedLevel) String() string { - return l.s -} - -// Set updates the value of the allowed level. -func (l *AllowedLevel) Set(s string) error { - switch s { - case "debug": - l.o = level.AllowDebug() - case "info": - l.o = level.AllowInfo() - case "warn": - l.o = level.AllowWarn() - case "error": - l.o = level.AllowError() - default: - return fmt.Errorf("unrecognized log level %q", s) - } - l.s = s - return nil -} - -// AllowedFormat is a settable identifier for the output format that the logger can have. -type AllowedFormat struct { - s string -} - -func (f *AllowedFormat) String() string { - return f.s -} - -// Set updates the value of the allowed format. -func (f *AllowedFormat) Set(s string) error { - switch s { - case "logfmt", "json": - f.s = s - default: - return fmt.Errorf("unrecognized log format %q", s) - } - return nil -} - -// Config is a struct containing configurable settings for the logger -type Config struct { - Level *AllowedLevel - Format *AllowedFormat -} - -// New returns a new leveled oklog logger. Each logged line will be annotated -// with a timestamp. The output always goes to stderr. -func New(config *Config) log.Logger { - if config.Format != nil && config.Format.s == "json" { - return NewWithLogger(log.NewJSONLogger(log.NewSyncWriter(os.Stderr)), config) - } - - return NewWithLogger(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), config) -} - -// NewWithLogger returns a new leveled oklog logger with a custom log.Logger. -// Each logged line will be annotated with a timestamp. -func NewWithLogger(l log.Logger, config *Config) log.Logger { - if config.Level != nil { - l = log.With(l, "ts", timestampFormat, "caller", log.Caller(5)) - l = level.NewFilter(l, config.Level.o) - } else { - l = log.With(l, "ts", timestampFormat, "caller", log.DefaultCaller) - } - return l -} - -// NewDynamic returns a new leveled logger. Each logged line will be annotated -// with a timestamp. The output always goes to stderr. Some properties can be -// changed, like the level. -func NewDynamic(config *Config) *logger { - if config.Format != nil && config.Format.s == "json" { - return NewDynamicWithLogger(log.NewJSONLogger(log.NewSyncWriter(os.Stderr)), config) - } - - return NewDynamicWithLogger(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), config) -} - -// NewDynamicWithLogger returns a new leveled logger with a custom io.Writer. -// Each logged line will be annotated with a timestamp. -// Some properties can be changed, like the level. -func NewDynamicWithLogger(l log.Logger, config *Config) *logger { - lo := &logger{ - base: l, - leveled: l, - } - - if config.Level != nil { - lo.SetLevel(config.Level) - } - - return lo -} - -type logger struct { - base log.Logger - leveled log.Logger - currentLevel *AllowedLevel - mtx sync.Mutex -} - -// Log implements logger.Log. -func (l *logger) Log(keyvals ...interface{}) error { - l.mtx.Lock() - defer l.mtx.Unlock() - return l.leveled.Log(keyvals...) -} - -// SetLevel changes the log level. -func (l *logger) SetLevel(lvl *AllowedLevel) { - l.mtx.Lock() - defer l.mtx.Unlock() - if lvl == nil { - l.leveled = log.With(l.base, "ts", timestampFormat, "caller", log.DefaultCaller) - l.currentLevel = nil - return - } - - if l.currentLevel != nil && l.currentLevel.s != lvl.s { - _ = l.base.Log("msg", "Log level changed", "prev", l.currentLevel, "current", lvl) - } - l.currentLevel = lvl - l.leveled = level.NewFilter(log.With(l.base, "ts", timestampFormat, "caller", log.Caller(5)), lvl.o) -} diff --git a/vendor/github.com/prometheus/common/promslog/slog.go b/vendor/github.com/prometheus/common/promslog/slog.go new file mode 100644 index 00000000..6e8fbabc --- /dev/null +++ b/vendor/github.com/prometheus/common/promslog/slog.go @@ -0,0 +1,201 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package promslog defines standardised ways to initialize the Go standard +// library's log/slog logger. +// It should typically only ever be imported by main packages. + +package promslog + +import ( + "fmt" + "io" + "log/slog" + "os" + "path/filepath" + "strconv" + "strings" +) + +type LogStyle string + +const ( + SlogStyle LogStyle = "slog" + GoKitStyle LogStyle = "go-kit" +) + +var ( + LevelFlagOptions = []string{"debug", "info", "warn", "error"} + FormatFlagOptions = []string{"logfmt", "json"} + + callerAddFunc = false + defaultWriter = os.Stderr + goKitStyleReplaceAttrFunc = func(groups []string, a slog.Attr) slog.Attr { + key := a.Key + switch key { + case slog.TimeKey: + a.Key = "ts" + + // This timestamp format differs from RFC3339Nano by using .000 instead + // of .999999999 which changes the timestamp from 9 variable to 3 fixed + // decimals (.130 instead of .130987456). + t := a.Value.Time() + a.Value = slog.StringValue(t.UTC().Format("2006-01-02T15:04:05.000Z07:00")) + case slog.SourceKey: + a.Key = "caller" + src, _ := a.Value.Any().(*slog.Source) + + switch callerAddFunc { + case true: + a.Value = slog.StringValue(filepath.Base(src.File) + "(" + filepath.Base(src.Function) + "):" + strconv.Itoa(src.Line)) + default: + a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) + } + case slog.LevelKey: + a.Value = slog.StringValue(strings.ToLower(a.Value.String())) + default: + } + + return a + } + defaultReplaceAttrFunc = func(groups []string, a slog.Attr) slog.Attr { + key := a.Key + switch key { + case slog.TimeKey: + t := a.Value.Time() + a.Value = slog.TimeValue(t.UTC()) + case slog.SourceKey: + src, _ := a.Value.Any().(*slog.Source) + a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) + default: + } + + return a + } +) + +// AllowedLevel is a settable identifier for the minimum level a log entry +// must be have. +type AllowedLevel struct { + s string + lvl *slog.LevelVar +} + +func (l *AllowedLevel) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + type plain string + if err := unmarshal((*plain)(&s)); err != nil { + return err + } + if s == "" { + return nil + } + lo := &AllowedLevel{} + if err := lo.Set(s); err != nil { + return err + } + *l = *lo + return nil +} + +func (l *AllowedLevel) String() string { + return l.s +} + +// Set updates the value of the allowed level. +func (l *AllowedLevel) Set(s string) error { + if l.lvl == nil { + l.lvl = &slog.LevelVar{} + } + + switch strings.ToLower(s) { + case "debug": + l.lvl.Set(slog.LevelDebug) + callerAddFunc = true + case "info": + l.lvl.Set(slog.LevelInfo) + callerAddFunc = false + case "warn": + l.lvl.Set(slog.LevelWarn) + callerAddFunc = false + case "error": + l.lvl.Set(slog.LevelError) + callerAddFunc = false + default: + return fmt.Errorf("unrecognized log level %s", s) + } + l.s = s + return nil +} + +// AllowedFormat is a settable identifier for the output format that the logger can have. +type AllowedFormat struct { + s string +} + +func (f *AllowedFormat) String() string { + return f.s +} + +// Set updates the value of the allowed format. +func (f *AllowedFormat) Set(s string) error { + switch s { + case "logfmt", "json": + f.s = s + default: + return fmt.Errorf("unrecognized log format %s", s) + } + return nil +} + +// Config is a struct containing configurable settings for the logger +type Config struct { + Level *AllowedLevel + Format *AllowedFormat + Style LogStyle + Writer io.Writer +} + +// New returns a new slog.Logger. Each logged line will be annotated +// with a timestamp. The output always goes to stderr. +func New(config *Config) *slog.Logger { + if config.Level == nil { + config.Level = &AllowedLevel{} + _ = config.Level.Set("info") + } + + if config.Writer == nil { + config.Writer = defaultWriter + } + + logHandlerOpts := &slog.HandlerOptions{ + Level: config.Level.lvl, + AddSource: true, + ReplaceAttr: defaultReplaceAttrFunc, + } + + if config.Style == GoKitStyle { + logHandlerOpts.ReplaceAttr = goKitStyleReplaceAttrFunc + } + + if config.Format != nil && config.Format.s == "json" { + return slog.New(slog.NewJSONHandler(config.Writer, logHandlerOpts)) + } + return slog.New(slog.NewTextHandler(config.Writer, logHandlerOpts)) +} + +// NewNopLogger is a convenience function to return an slog.Logger that writes +// to io.Discard. +func NewNopLogger() *slog.Logger { + return slog.New(slog.NewTextHandler(io.Discard, nil)) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 7b1bb16e..5bf2ebec 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -84,13 +84,6 @@ github.com/davecgh/go-spew/spew # github.com/felixge/httpsnoop v1.0.4 ## explicit; go 1.13 github.com/felixge/httpsnoop -# github.com/go-kit/log v0.2.1 -## explicit; go 1.17 -github.com/go-kit/log -github.com/go-kit/log/level -# github.com/go-logfmt/logfmt v0.5.1 -## explicit; go 1.17 -github.com/go-logfmt/logfmt # github.com/go-logr/logr v1.4.2 ## explicit; go 1.18 github.com/go-logr/logr @@ -219,7 +212,7 @@ github.com/prometheus/client_model/go ## explicit; go 1.21 github.com/prometheus/common/expfmt github.com/prometheus/common/model -github.com/prometheus/common/promlog +github.com/prometheus/common/promslog # github.com/prometheus/procfs v0.15.1 ## explicit; go 1.20 github.com/prometheus/procfs