2022-09-29 20:43:20 +01:00
package logging
import (
2022-10-05 11:37:52 +02:00
"context"
2022-09-29 20:43:20 +01:00
"errors"
"flag"
2022-10-19 15:12:55 +02:00
"io"
stdlog "log"
2022-09-29 20:43:20 +01:00
"os"
2022-10-19 15:12:55 +02:00
"strings"
2022-09-29 20:43:20 +01:00
"github.com/go-logr/logr"
"github.com/go-logr/zapr"
"go.uber.org/zap"
2022-11-09 16:10:27 +01:00
"go.uber.org/zap/zapcore"
2022-09-29 20:43:20 +01:00
"k8s.io/klog/v2"
"k8s.io/klog/v2/klogr"
"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 16:42:43 +02:00
// LogLevelController is the log level to use for controllers plumbing.
LogLevelController = 3
2022-11-18 15:21:15 +01:00
// LogLevelClient is the log level to use for clients.
LogLevelClient = 3
2022-09-29 20:43:20 +01:00
)
2022-10-06 20:37:10 +01: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.
var globalLog = log . Log
2022-11-18 15:21:15 +01:00
func InitFlags ( flags * flag . FlagSet ) {
2022-09-29 20:43:20 +01: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.
2022-11-09 16:10:27 +01:00
func Setup ( logFormat string , level int ) error {
2022-09-29 20:43:20 +01:00
switch logFormat {
case TextFormat :
// in text mode we use FormatSerialize format
2022-10-06 20:37:10 +01:00
globalLog = klogr . New ( )
2022-09-29 20:43:20 +01:00
case JSONFormat :
2022-11-09 16:10:27 +01:00
zc := zap . NewProductionConfig ( )
// 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 ( )
2022-09-29 20:43:20 +01:00
if err != nil {
2022-09-30 08:13:13 +02:00
return err
2022-09-29 20:43:20 +01:00
}
2022-10-28 21:52:58 +01:00
globalLog = zapr . NewLogger ( zapLog )
// in json mode we configure klog and global logger to use zapr
klog . SetLogger ( globalLog . WithName ( "klog" ) )
2022-09-30 08:13:13 +02:00
default :
return errors . New ( "log format not recognized, pass `text` for text mode or `json` to enable JSON logging" )
2022-09-29 20:43:20 +01:00
}
2022-10-06 20:37:10 +01:00
log . SetLogger ( globalLog )
2022-09-30 08:13:13 +02:00
return nil
2022-09-29 20:43:20 +01:00
}
// GlobalLogger returns a logr.Logger as configured in main.
func GlobalLogger ( ) logr . Logger {
2022-10-06 20:37:10 +01:00
return globalLog
2022-09-29 20:43:20 +01:00
}
2022-10-18 16:42:43 +02: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 15:21:15 +01: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 20:43:20 +01: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 20:37:10 +01:00
GlobalLogger ( ) . WithCallDepth ( 1 ) . Info ( msg , keysAndValues ... )
2022-09-29 20:43:20 +01:00
}
// Error logs an error, with the given message and key/value pairs.
func Error ( err error , msg string , keysAndValues ... interface { } ) {
2022-10-06 20:37:10 +01:00
GlobalLogger ( ) . WithCallDepth ( 1 ) . Error ( err , msg , keysAndValues ... )
2022-09-29 20:43:20 +01:00
}
2022-10-05 11:37:52 +02: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 15:12:55 +02: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 )
}