2022-09-29 19:43:20 +00:00
package logging
import (
2022-10-05 09:37:52 +00:00
"context"
2022-09-29 19:43:20 +00:00
"errors"
"flag"
2022-10-19 13:12:55 +00:00
"io"
stdlog "log"
2022-09-29 19:43:20 +00:00
"os"
2022-10-19 13:12:55 +00:00
"strings"
2022-09-29 19:43:20 +00:00
"github.com/go-logr/logr"
"github.com/go-logr/zapr"
"go.uber.org/zap"
2022-11-09 15:10:27 +00:00
"go.uber.org/zap/zapcore"
2022-09-29 19:43:20 +00:00
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/log"
)
const (
// JSONFormat represents JSON logging mode.
JSONFormat = "json"
// TextFormat represents text logging mode.
// Default logging mode is TextFormat.
TextFormat = "text"
2022-10-18 14:42:43 +00:00
// LogLevelController is the log level to use for controllers plumbing.
2023-06-13 14:23:53 +00:00
LogLevelController = 1
2022-11-18 14:21:15 +00:00
// LogLevelClient is the log level to use for clients.
2023-06-13 14:23:53 +00:00
LogLevelClient = 1
2024-02-01 19:14:47 +00:00
// time formats
DefaultTime = "default"
ISO8601 = "iso8601"
RFC3339 = "rfc3339"
MILLIS = "millis"
NANOS = "nanos"
EPOCH = "epoch"
RFC3339NANO = "rfc3339nano"
2022-09-29 19:43:20 +00:00
)
2022-10-06 19:37:10 +00:00
// Initially, globalLog comes from controller-runtime/log with logger created earlier by controller-runtime.
// When logging.Setup is called, globalLog is switched to the real logger.
// Call depth of all loggers created before logging.Setup will not work, including package level loggers as they are created before main.
// All loggers created after logging.Setup won't be subject to the call depth limitation and will work if the underlying sink supports it.
2024-02-01 19:14:47 +00:00
var globalLog = log . Log // returns a Null log sink if SetLogger is not called.
2022-10-06 19:37:10 +00:00
2022-11-18 14:21:15 +00:00
func InitFlags ( flags * flag . FlagSet ) {
2022-09-29 19:43:20 +00:00
// clear flags initialized in static dependencies
if flag . CommandLine . Lookup ( "log_dir" ) != nil {
flag . CommandLine = flag . NewFlagSet ( os . Args [ 0 ] , flag . ExitOnError )
}
klog . InitFlags ( flags )
}
// Setup configures the logger with the supplied log format.
// It returns an error if the JSON logger could not be initialized or passed logFormat is not recognized.
2024-02-01 19:14:47 +00:00
func Setup ( logFormat string , loggingTimestampFormat string , level int ) error {
var zc zap . Config
2022-09-29 19:43:20 +00:00
switch logFormat {
case TextFormat :
2024-02-01 19:14:47 +00:00
zc = zap . NewDevelopmentConfig ( )
2022-09-29 19:43:20 +00:00
case JSONFormat :
2024-02-01 19:14:47 +00:00
zc = zap . NewProductionConfig ( )
2022-09-30 06:13:13 +00:00
default :
return errors . New ( "log format not recognized, pass `text` for text mode or `json` to enable JSON logging" )
2022-09-29 19:43:20 +00:00
}
2024-02-01 19:14:47 +00:00
// configure the timestamp format
switch loggingTimestampFormat {
case ISO8601 :
zc . EncoderConfig . EncodeTime = zapcore . ISO8601TimeEncoder
case RFC3339 :
zc . EncoderConfig . EncodeTime = zapcore . RFC3339TimeEncoder
case MILLIS :
zc . EncoderConfig . EncodeTime = zapcore . EpochMillisTimeEncoder
case NANOS :
zc . EncoderConfig . EncodeTime = zapcore . EpochNanosTimeEncoder
case EPOCH :
zc . EncoderConfig . EncodeTime = zapcore . EpochTimeEncoder
case RFC3339NANO :
zc . EncoderConfig . EncodeTime = zapcore . RFC3339NanoTimeEncoder
case "default" :
2024-02-23 10:01:42 +00:00
zc . EncoderConfig . EncodeTime = zapcore . RFC3339TimeEncoder
2024-02-01 19:14:47 +00:00
default :
return errors . New ( "timestamp format not recognized, pass `iso8601` for ISO8601, `rfc3339` for RFC3339, `rfc3339nano` for RFC3339NANO, `millis` for Epoch Millis, `nanos` for Epoch Nanos, or omit the flag for the Unix Epoch timestamp format" )
}
// Zap's levels get more and less verbose as the number gets smaller and higher respectively (DebugLevel is -1, InfoLevel is 0, WarnLevel is 1, and so on).
zc . Level = zap . NewAtomicLevelAt ( zapcore . Level ( - 1 * level ) )
zapLog , err := zc . Build ( )
if err != nil {
return err
}
globalLog = zapr . NewLogger ( zapLog )
// in json mode we configure klog and global logger to use zapr
klog . SetLogger ( globalLog . WithName ( "klog" ) )
2022-10-06 19:37:10 +00:00
log . SetLogger ( globalLog )
2022-09-30 06:13:13 +00:00
return nil
2022-09-29 19:43:20 +00:00
}
// GlobalLogger returns a logr.Logger as configured in main.
func GlobalLogger ( ) logr . Logger {
2022-10-06 19:37:10 +00:00
return globalLog
2022-09-29 19:43:20 +00:00
}
2022-10-18 14:42:43 +00:00
// ControllerLogger returns a logr.Logger to be used by controllers.
func ControllerLogger ( name string ) logr . Logger {
return globalLog . WithName ( name ) . V ( LogLevelController )
}
2022-11-18 14:21:15 +00:00
// ClientLogger returns a logr.Logger to be used by clients.
func ClientLogger ( name string ) logr . Logger {
return globalLog . WithName ( name ) . V ( LogLevelClient )
}
2022-09-29 19:43:20 +00:00
// WithName returns a new logr.Logger instance with the specified name element added to the Logger's name.
func WithName ( name string ) logr . Logger {
return GlobalLogger ( ) . WithName ( name )
}
// WithValues returns a new logr.Logger instance with additional key/value pairs.
func WithValues ( keysAndValues ... interface { } ) logr . Logger {
return GlobalLogger ( ) . WithValues ( keysAndValues ... )
}
// V returns a new logr.Logger instance for a specific verbosity level.
func V ( level int ) logr . Logger {
return GlobalLogger ( ) . V ( level )
}
// Info logs a non-error message with the given key/value pairs.
func Info ( msg string , keysAndValues ... interface { } ) {
2022-10-06 19:37:10 +00:00
GlobalLogger ( ) . WithCallDepth ( 1 ) . Info ( msg , keysAndValues ... )
2022-09-29 19:43:20 +00:00
}
// Error logs an error, with the given message and key/value pairs.
func Error ( err error , msg string , keysAndValues ... interface { } ) {
2022-10-06 19:37:10 +00:00
GlobalLogger ( ) . WithCallDepth ( 1 ) . Error ( err , msg , keysAndValues ... )
2022-09-29 19:43:20 +00:00
}
2022-10-05 09:37:52 +00:00
// FromContext returns a logger with predefined values from a context.Context.
func FromContext ( ctx context . Context , keysAndValues ... interface { } ) ( logr . Logger , error ) {
logger , err := logr . FromContext ( ctx )
if err != nil {
return logger , err
}
return logger . WithValues ( keysAndValues ... ) , nil
}
// IntoContext takes a context and sets the logger as one of its values.
// Use FromContext function to retrieve the logger.
func IntoContext ( ctx context . Context , log logr . Logger ) context . Context {
return logr . NewContext ( ctx , log )
}
// IntoBackground calls IntoContext with the logger and a Background context.
func IntoBackground ( log logr . Logger ) context . Context {
return IntoContext ( context . Background ( ) , log )
}
// IntoTODO calls IntoContext with the logger and a TODO context.
func IntoTODO ( log logr . Logger ) context . Context {
return IntoContext ( context . TODO ( ) , log )
}
// Background calls IntoContext with the global logger and a Background context.
func Background ( ) context . Context {
return IntoContext ( context . Background ( ) , GlobalLogger ( ) )
}
// TODO calls IntoContext with the global logger and a TODO context.
func TODO ( ) context . Context {
return IntoContext ( context . TODO ( ) , GlobalLogger ( ) )
}
2022-10-19 13:12:55 +00:00
type writerAdapter struct {
io . Writer
logger logr . Logger
}
func ( a * writerAdapter ) Write ( p [ ] byte ) ( int , error ) {
a . logger . Info ( strings . TrimSuffix ( string ( p ) , "\n" ) )
return len ( p ) , nil
}
func StdLogger ( logger logr . Logger , prefix string ) * stdlog . Logger {
return stdlog . New ( & writerAdapter { logger : logger } , prefix , stdlog . LstdFlags )
}