diff --git a/README.md b/README.md index 43ff7e75..954cc91b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ any stability guarantees for external usage. * **config**: Common configuration structures * **expfmt**: Decoding and encoding for the exposition format * **model**: Shared data structures -* **promlog**: A logging wrapper around [go-kit/log](https://github.com/go-kit/kit/tree/master/log) * **promslog**: A logging wrapper around [log/slog](https://pkg.go.dev/log/slog) * **route**: A routing wrapper around [httprouter](https://github.com/julienschmidt/httprouter) using `context.Context` * **server**: Common servers diff --git a/promlog/flag/flag.go b/promlog/flag/flag.go deleted file mode 100644 index 16352a0f..00000000 --- a/promlog/flag/flag.go +++ /dev/null @@ -1,53 +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. - -// 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 flag - -import ( - "strings" - - kingpin "github.com/alecthomas/kingpin/v2" - - "github.com/prometheus/common/promlog" //nolint:staticcheck -) - -// LevelFlagName is the canonical flag name to configure the allowed log level -// within Prometheus projects. -const LevelFlagName = "log.level" - -// LevelFlagHelp is the help description for the log.level flag. -var LevelFlagHelp = "Only log messages with the given severity or above. One of: [" + strings.Join(promlog.LevelFlagOptions, ", ") + "]" - -// FormatFlagName is the canonical flag name to configure the log format -// within Prometheus projects. -const FormatFlagName = "log.format" - -// FormatFlagHelp is the help description for the log.format flag. -var FormatFlagHelp = "Output format of log messages. One of: [" + strings.Join(promlog.FormatFlagOptions, ", ") + "]" - -// AddFlags adds the flags used by this package to the Kingpin application. -// To use the default Kingpin application, call AddFlags(kingpin.CommandLine) -func AddFlags(a *kingpin.Application, config *promlog.Config) { - config.Level = &promlog.AllowedLevel{} - a.Flag(LevelFlagName, LevelFlagHelp). - Default("info").HintOptions(promlog.LevelFlagOptions...). - SetValue(config.Level) - - config.Format = &promlog.AllowedFormat{} - a.Flag(FormatFlagName, FormatFlagHelp). - Default("logfmt").HintOptions(promlog.FormatFlagOptions...). - SetValue(config.Format) -} diff --git a/promlog/log.go b/promlog/log.go deleted file mode 100644 index 24a11622..00000000 --- a/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/promlog/log_test.go b/promlog/log_test.go deleted file mode 100644 index 395e0516..00000000 --- a/promlog/log_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2020 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 - -import ( - "fmt" - "testing" - - "github.com/go-kit/log/level" - "github.com/stretchr/testify/require" - "gopkg.in/yaml.v2" -) - -// Make sure creating and using a logger with an empty configuration doesn't -// result in a panic. -func TestDefaultConfig(t *testing.T) { - logger := New(&Config{}) - - err := logger.Log("hello", "world") - require.NoError(t, err) -} - -func TestUnmarshallLevel(t *testing.T) { - l := &AllowedLevel{} - err := yaml.Unmarshal([]byte(`debug`), l) - if err != nil { - t.Error(err) - } - if l.s != "debug" { - t.Errorf("expected %s, got %s", "debug", l.s) - } -} - -func TestUnmarshallEmptyLevel(t *testing.T) { - l := &AllowedLevel{} - err := yaml.Unmarshal([]byte(``), l) - if err != nil { - t.Error(err) - } - if l.s != "" { - t.Errorf("expected empty level, got %s", l.s) - } -} - -func TestUnmarshallBadLevel(t *testing.T) { - l := &AllowedLevel{} - err := yaml.Unmarshal([]byte(`debugg`), l) - if err == nil { - t.Error("expected error") - } - expErr := `unrecognized log level "debugg"` - if err.Error() != expErr { - t.Errorf("expected error %s, got %s", expErr, err.Error()) - } - if l.s != "" { - t.Errorf("expected empty level, got %s", l.s) - } -} - -type recordKeyvalLogger struct { - count int -} - -func (r *recordKeyvalLogger) Log(keyvals ...interface{}) error { - for _, v := range keyvals { - if fmt.Sprintf("%v", v) == "Log level changed" { - return nil - } - } - r.count++ - return nil -} - -func TestDynamic(t *testing.T) { - logger := NewDynamic(&Config{}) - - debugLevel := &AllowedLevel{} - err := debugLevel.Set("debug") - require.NoError(t, err) - infoLevel := &AllowedLevel{} - err = infoLevel.Set("info") - require.NoError(t, err) - - recorder := &recordKeyvalLogger{} - logger.base = recorder - logger.SetLevel(debugLevel) - err = level.Debug(logger).Log("hello", "world") - require.NoError(t, err) - require.Equalf(t, 1, recorder.count, "log not found") - - recorder.count = 0 - logger.SetLevel(infoLevel) - err = level.Debug(logger).Log("hello", "world") - require.NoError(t, err) - require.Equalf(t, 0, recorder.count, "log found") - err = level.Info(logger).Log("hello", "world") - require.NoError(t, err) - require.Equalf(t, 1, recorder.count, "log not found") - err = level.Debug(logger).Log("hello", "world") - require.NoError(t, err) - require.Equalf(t, 1, recorder.count, "extra log found") -}