Skip to content

Commit

Permalink
Setup Authorino logger (#158)
Browse files Browse the repository at this point in the history
  • Loading branch information
grzpiotrowski authored Nov 17, 2023
1 parent d1f1a05 commit ff5f358
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 6 deletions.
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ require (
github.com/go-logr/logr v1.2.4
github.com/onsi/ginkgo/v2 v2.11.0
github.com/onsi/gomega v1.27.10
go.uber.org/zap v1.25.0
gotest.tools v2.2.0+incompatible
k8s.io/api v0.28.3
k8s.io/apimachinery v0.28.3
k8s.io/client-go v0.28.3
k8s.io/klog/v2 v2.100.1
sigs.k8s.io/controller-runtime v0.16.3
)

Expand Down Expand Up @@ -47,7 +50,6 @@ require (
github.com/prometheus/procfs v0.10.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.25.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
Expand All @@ -64,7 +66,6 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.28.3 // indirect
k8s.io/component-base v0.28.3 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08=
Expand Down
28 changes: 24 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"

"github.com/kuadrant/authorino-operator/pkg/log"

authorinooperatorv1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1"
"github.com/kuadrant/authorino-operator/controllers"
//+kubebuilder:scaffold:imports
Expand All @@ -41,8 +43,8 @@ import (
var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")

version string // value injected in compilation-time
logger log.Logger
version string // value injected in compilation-time
)

func init() {
Expand All @@ -52,24 +54,42 @@ func init() {
//+kubebuilder:scaffold:scheme
}

type logOptions struct {
level string
mode string
}

func setupLogger(opts logOptions) {
logOpts := log.Options{Level: log.ToLogLevel(opts.level), Mode: log.ToLogMode(opts.mode)}
logger = log.NewLogger(logOpts).WithName("authorino-operator").WithName("controller").WithName("Authorino")
log.SetLogger(logger)
}

func main() {
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
logOpts := logOptions{}

flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
flag.StringVar(&logOpts.level, "log-level", "info", "Log level (info, debug, error, etc.)")
flag.StringVar(&logOpts.mode, "log-mode", "production", "Log mode (development or production)")

opts := zap.Options{
Development: true,
}
opts.BindFlags(flag.CommandLine)
flag.Parse()

setupLogger(logOpts)

ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

setupLog.Info("botting up authorino operator", "version", version, "default authorino image", controllers.DefaultAuthorinoImage)
setupLog.Info("booting up authorino operator", "version", version, "default authorino image", controllers.DefaultAuthorinoImage)

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Expand All @@ -86,7 +106,7 @@ func main() {

if err = (&controllers.AuthorinoReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("authorino-operator").WithName("controller").WithName("Authorino"),
Log: logger,
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Authorino")
Expand Down
129 changes: 129 additions & 0 deletions pkg/log/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package log

import (
"context"
"strings"

"github.com/go-logr/logr"
"go.uber.org/zap/zapcore"
"k8s.io/klog/v2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)

var (
// Log is a singleton base logger that can be used across the system,
// either directly or to create other loggers with name, with values,
// and/or locked to a given log level.
// It is initialized to the promise delegation log provided by
// sigs.k8s.io/controller-runtime, which points to a no-op (null) logger
// until `SetLogger` is called.
// This is also useful for mocking the default logger tests.
Log Logger = ctrl.Log
)

type Logger = logr.Logger

type LogLevel zapcore.Level

func (l *LogLevel) String() string {
return zapcore.Level(*l).String()
}

// ToLogLevel converts a string to a log level.
func ToLogLevel(level string) LogLevel {
var l zapcore.Level
_ = l.UnmarshalText([]byte(level))
return LogLevel(l)
}

// LogMode defines the log output mode.
type LogMode int8

const (
// LogModeProd is the log mode for production.
LogModeProd LogMode = iota
// LogModeDev is for more human-readable outputs, extra stack traces
// and logging info. (aka Zap's "development config".)
LogModeDev
)

func (f *LogMode) String() string {
switch *f {
case LogModeProd:
return "production"
case LogModeDev:
return "development"
default:
return "unknown"
}
}

// ToLogMode converts a string to a log mode.
// Use either 'production' for `LogModeProd` or 'development' for `LogModeDev`.
func ToLogMode(mode string) LogMode {
switch strings.ToLower(mode) {
case "production":
return LogModeProd
case "development":
return LogModeDev
default:
panic("unknown log mode")
}
}

// Options is a set of options for a configured logger.
type Options struct {
Level LogLevel
Mode LogMode
}

// SetLogger sets up a logger.
func SetLogger(logger Logger) {
Log = logger

ctrl.SetLogger(Log) // fulfills `logger` as the de facto logger used by controller-runtime
klog.SetLogger(Log)
}

// WithName uses the singleton logger to create a new logger with the given name.
func WithName(name string) Logger {
return Log.WithName(name)
}

// WithName uses the singleton logger to create a new logger with the given values.
func WithValues(keysAndValues ...interface{}) Logger {
return Log.WithValues(keysAndValues...)
}

// V uses the singleton logger to create a new logger for the given log level.
func V(level int) Logger {
return Log.V(level)
}

// 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 Logger) context.Context {
return logr.NewContext(ctx, log)
}

// FromContext returns a logger with predefined values from a context.Context.
func FromContext(ctx context.Context, keysAndValues ...interface{}) Logger {
var l logr.Logger = Log
if ctx != nil {
if logger, err := logr.FromContext(ctx); err == nil {
l = logger
}
}
return l.WithValues(keysAndValues...)
}

// NewLogger returns a new logger with the given options.
// `logger` param is the actual logger implementation; when omitted, a new
// logger based on sigs.k8s.io/controller-runtime/pkg/log/zap is created.
func NewLogger(opts Options) Logger {
return zap.New(
zap.Level(zapcore.Level(opts.Level)),
zap.UseDevMode(opts.Mode == LogModeDev),
)
}
61 changes: 61 additions & 0 deletions pkg/log/logger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package log

import (
"testing"

"gotest.tools/assert"
)

func TestLogLevelToString(t *testing.T) {
level := LogLevel(-1)
assert.Equal(t, level.String(), "debug")

level = LogLevel(0)
assert.Equal(t, level.String(), "info")

level = LogLevel(1)
assert.Equal(t, level.String(), "warn")

level = LogLevel(2)
assert.Equal(t, level.String(), "error")

level = LogLevel(3)
assert.Equal(t, level.String(), "dpanic")

level = LogLevel(4)
assert.Equal(t, level.String(), "panic")

level = LogLevel(5)
assert.Equal(t, level.String(), "fatal")
}

func TestToLogLevel(t *testing.T) {
assert.Equal(t, int(ToLogLevel("debug")), -1)
assert.Equal(t, int(ToLogLevel("info")), 0)
assert.Equal(t, int(ToLogLevel("warn")), 1)
assert.Equal(t, int(ToLogLevel("error")), 2)
assert.Equal(t, int(ToLogLevel("dpanic")), 3)
assert.Equal(t, int(ToLogLevel("panic")), 4)
assert.Equal(t, int(ToLogLevel("fatal")), 5)
assert.Equal(t, int(ToLogLevel("invalid")), 0) // falls back to default log level (info) without panicking
}

func TestLogModeToString(t *testing.T) {
level := LogMode(0)
assert.Equal(t, level.String(), "production")

level = LogMode(1)
assert.Equal(t, level.String(), "development")
}

func TestToLogMode(t *testing.T) {
assert.Equal(t, int(ToLogMode("production")), 0)
assert.Equal(t, int(ToLogMode("development")), 1)

defer func() {
if r := recover(); r == nil {
t.Errorf(`ToLogMode("invalid") was expected to panic and it did not.`)
}
}()
_ = ToLogMode("invalid")
}

0 comments on commit ff5f358

Please sign in to comment.