Add support for configuration static logging fields
To allow flexibility in log message context information, this changeset provides the ability to configure static fields that are included in the context. Such fields can be set via configuration or environment variables. Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
parent
a75f0f26f7
commit
ac73963d7e
5 changed files with 89 additions and 12 deletions
|
@ -1,6 +1,9 @@
|
|||
version: 0.1
|
||||
log:
|
||||
level: debug
|
||||
fields:
|
||||
service: registry
|
||||
environment: development
|
||||
storage:
|
||||
filesystem:
|
||||
rootdirectory: /tmp/registry-dev
|
||||
|
|
|
@ -50,13 +50,11 @@ func main() {
|
|||
fatalf("configuration error: %v", err)
|
||||
}
|
||||
|
||||
if err := configureLogging(config); err != nil {
|
||||
ctx, err = configureLogging(ctx, config)
|
||||
if err != nil {
|
||||
fatalf("error configuring logger: %v", err)
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, "version", version.Version)
|
||||
ctx = ctxu.WithLogger(ctx, ctxu.GetLogger(ctx, "version"))
|
||||
|
||||
app := handlers.NewApp(ctx, *config)
|
||||
handler := configureReporting(app)
|
||||
handler = gorhandlers.CombinedLoggingHandler(os.Stdout, handler)
|
||||
|
@ -151,13 +149,14 @@ func configureReporting(app *handlers.App) http.Handler {
|
|||
return handler
|
||||
}
|
||||
|
||||
// configureLogging the default logger using the configuration. Must be called
|
||||
// before creating any contextual loggers.
|
||||
func configureLogging(config *configuration.Configuration) error {
|
||||
// configureLogging prepares the context with a logger using the
|
||||
// configuration.
|
||||
func configureLogging(ctx context.Context, config *configuration.Configuration) (context.Context, error) {
|
||||
if config.Log.Level == "" && config.Log.Formatter == "" {
|
||||
// If no config for logging is set, fallback to deprecated "Loglevel".
|
||||
log.SetLevel(logLevel(config.Loglevel))
|
||||
return nil
|
||||
ctx = ctxu.WithLogger(ctx, ctxu.GetLogger(ctx, "version"))
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
log.SetLevel(logLevel(config.Log.Level))
|
||||
|
@ -168,11 +167,11 @@ func configureLogging(config *configuration.Configuration) error {
|
|||
case "text":
|
||||
log.SetFormatter(&log.TextFormatter{})
|
||||
case "logstash":
|
||||
log.SetFormatter(&logstash.LogstashFormatter{Type: "registry"})
|
||||
log.SetFormatter(&logstash.LogstashFormatter{})
|
||||
default:
|
||||
// just let the library use default on empty string.
|
||||
if config.Log.Formatter != "" {
|
||||
return fmt.Errorf("unsupported logging formatter: %q", config.Log.Formatter)
|
||||
return ctx, fmt.Errorf("unsupported logging formatter: %q", config.Log.Formatter)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,7 +179,21 @@ func configureLogging(config *configuration.Configuration) error {
|
|||
log.Debugf("using %q logging formatter", config.Log.Formatter)
|
||||
}
|
||||
|
||||
return nil
|
||||
// log the application version with messages
|
||||
ctx = context.WithValue(ctx, "version", version.Version)
|
||||
|
||||
if len(config.Log.Fields) > 0 {
|
||||
// build up the static fields, if present.
|
||||
var fields []interface{}
|
||||
for k := range config.Log.Fields {
|
||||
fields = append(fields, k)
|
||||
}
|
||||
|
||||
ctx = withMapContext(ctx, config.Log.Fields)
|
||||
ctx = ctxu.WithLogger(ctx, ctxu.GetLogger(ctx, fields...))
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func logLevel(level configuration.Loglevel) log.Level {
|
||||
|
@ -193,6 +206,36 @@ func logLevel(level configuration.Loglevel) log.Level {
|
|||
return l
|
||||
}
|
||||
|
||||
// stringMapContext is a simple context implementation that checks a map for a
|
||||
// key, falling back to a parent if not present.
|
||||
type stringMapContext struct {
|
||||
context.Context
|
||||
m map[string]string
|
||||
}
|
||||
|
||||
// withMapContext returns a context that proxies lookups through a map.
|
||||
func withMapContext(ctx context.Context, m map[string]string) context.Context {
|
||||
mo := make(map[string]string, len(m)) // make our own copy.
|
||||
for k, v := range m {
|
||||
mo[k] = v
|
||||
}
|
||||
|
||||
return stringMapContext{
|
||||
Context: ctx,
|
||||
m: mo,
|
||||
}
|
||||
}
|
||||
|
||||
func (smc stringMapContext) Value(key interface{}) interface{} {
|
||||
if ks, ok := key.(string); ok {
|
||||
if v, ok := smc.m[ks]; ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return smc.Context.Value(key)
|
||||
}
|
||||
|
||||
// debugServer starts the debug server with pprof, expvar among other
|
||||
// endpoints. The addr should not be exposed externally. For most of these to
|
||||
// work, tls cannot be enabled on the endpoint, so it is generally separate.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue