// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package plog import ( "context" "encoding/json" "strconv" "time" "go.uber.org/zap/zapcore" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/component-base/logs" "go.pinniped.dev/internal/constable" ) type LogFormat string func (l *LogFormat) UnmarshalJSON(b []byte) error { switch string(b) { case `""`, `"json"`: *l = FormatJSON case `"text"`: *l = FormatText // there is no "cli" case because it is not a supported option via our config default: return errInvalidLogFormat } return nil } const ( FormatJSON LogFormat = "json" FormatText LogFormat = "text" FormatCLI LogFormat = "cli" // only used by the pinniped CLI and not the server components errInvalidLogLevel = constable.Error("invalid log level, valid choices are the empty string, info, debug, trace and all") errInvalidLogFormat = constable.Error("invalid log format, valid choices are the empty string, json and text") ) var _ json.Unmarshaler = func() *LogFormat { var f LogFormat return &f }() type LogSpec struct { Level LogLevel `json:"level,omitempty"` Format LogFormat `json:"format,omitempty"` } func MaybeSetDeprecatedLogLevel(level *LogLevel, log *LogSpec) { if level != nil { Warning("logLevel is deprecated, set log.level instead") log.Level = *level } } func ValidateAndSetLogLevelAndFormatGlobally(ctx context.Context, spec LogSpec) error { klogLevel := klogLevelForPlogLevel(spec.Level) if klogLevel < 0 { return errInvalidLogLevel } // set the global log levels used by our code and the kube code underneath us if _, err := logs.GlogSetter(strconv.Itoa(int(klogLevel))); err != nil { panic(err) // programmer error } globalLevel.SetLevel(zapcore.Level(-klogLevel)) // klog levels are inverted when zap handles them var encoding string switch spec.Format { case "", FormatJSON: encoding = "json" case FormatCLI: encoding = "console" case FormatText: encoding = "text" default: return errInvalidLogFormat } log, flush, err := newLogr(ctx, encoding, klogLevel) if err != nil { return err } setGlobalLoggers(log, flush) //nolint:exhaustive // the switch above is exhaustive for format already switch spec.Format { case FormatCLI: return nil // do not spawn go routines on the CLI to allow the CLI to call this more than once case FormatText: Warning("setting log.format to 'text' is deprecated - this option will be removed in a future release") } // do spawn go routines on the server go wait.UntilWithContext(ctx, func(_ context.Context) { flush() }, time.Minute) go func() { <-ctx.Done() flush() // best effort flush before shutdown as this is not coordinated with a wait group }() return nil }