在运行时更新zap日志记录器的日志级别是否可能?

huangapple go评论103阅读模式
英文:

Is it possible to update the log level of a zap logger at runtime?

问题

我使用kubebuilder创建了一个基于zap logger的记录器:

import (
	"flag"
	"github.com/gin-gonic/gin"
	"net/http"
	"os"
	"go.uber.org/zap/zapcore"
    uzap "go.uber.org/zap"
	// 导入所有Kubernetes客户端认证插件(例如Azure、GCP、OIDC等)
	// 以确保exec-entrypoint和run可以使用它们。
	_ "k8s.io/client-go/plugin/pkg/client/auth"

	"k8s.io/apimachinery/pkg/runtime"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/healthz"
	"sigs.k8s.io/controller-runtime/pkg/log/zap"
)

var (
	scheme   = runtime.NewScheme()
	setupLog = ctrl.Log.WithName("setup")
)

var zapOpts []uzap.Option
	zapOpts = append(zapOpts, uzap.AddCaller())
	zapOpts = append(zapOpts, uzap.AddCallerSkip(1))
	zapOpts = append(zapOpts, uzap.AddStacktrace(uzap.DebugLevel))

	opts := zap.Options{
		Development:     developmentFlag,
		StacktraceLevel: stacktraceLevel,
		Level:           level,
		Encoder:         encoder,
		ZapOpts:  zapOpts,
	}

	opts.BindFlags(flag.CommandLine)
	flag.Parse()

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

现在我想在运行时将日志级别更改为zapcore.InfoLevel。我没有找到任何SetLogLevel或类似的API。

我需要创建新的opts然后设置新的级别吗?

另外,我需要使用sigs.k8s.io/controller-runtime/pkg/log/zap库设置记录器。记录器的接口来自于go-logr,它实现了logr.Logger接口。如果我尝试将其更改为zapcore.NewCore,那么我将无法再使用ctrl.SetLogger设置记录器。

我希望保留更新zap.Options的所有选项,并更改日志级别,同时仍然使用来自sigs.k8s.io/controller-runtime/pkg/log/zap的zap。

是否可以使用sigs.k8s.io/controller-runtime/pkg/log/zapsigs.k8s.io/controller-runtime来实现这一点?

英文:

I created a logger with kubebuilder, it is based on zap logger:

import (
	"flag"
	"github.com/gin-gonic/gin"
	"net/http"
	"os"
	"go.uber.org/zap/zapcore"
    uzap "go.uber.org/zap"
	// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
	// to ensure that exec-entrypoint and run can make use of them.
	_ "k8s.io/client-go/plugin/pkg/client/auth"

	"k8s.io/apimachinery/pkg/runtime"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/healthz"
	"sigs.k8s.io/controller-runtime/pkg/log/zap"

)

var (
	scheme   = runtime.NewScheme()
	setupLog = ctrl.Log.WithName("setup")
)

var zapOpts []uzap.Option
	zapOpts = append(zapOpts, uzap.AddCaller())
	zapOpts = append(zapOpts, uzap.AddCallerSkip(1))
	zapOpts = append(zapOpts, uzap.AddStacktrace(uzap.DebugLevel))

	opts := zap.Options{
		Development:     developmentFlag,
		StacktraceLevel: stacktraceLevel,
		Level:           level,
		Encoder:         encoder,
		ZapOpts:  zapOpts,
	}

	opts.BindFlags(flag.CommandLine)
	flag.Parse()

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

Now I want to change the log level to zapcore.InfoLevel at run time. I didn't find any SetLogLevel or similar API.

Do I need to create new opts and then set the new level?

Also I need to set the logger with sigs.k8s.io/controller-runtime/pkg/log/zap library. The interface of the logger is from go-logr and it implements logr.Logger interface. If I tried to change it to zapcore.NewCore than I can't set the logger with ctrl.SetLogger anymore.

I want to keep the options to update all the options of zap.Options and also to change the log level, and still to use the zap from sigs.k8s.io/controller-runtime/pkg/log/zap.

Is it possible to do it with
sigs.k8s.io/controller-runtime/pkg/log/zap
and sigs.k8s.io/controller-runtime?

答案1

得分: 7

是的,可以使用AtomicLevel实现。根据文档:

atom := zap.NewAtomicLevel()

// 为了保持示例的确定性,在输出中禁用时间戳。
encoderCfg := zap.NewProductionEncoderConfig()
encoderCfg.TimeKey = ""

logger := zap.New(zapcore.NewCore(
	zapcore.NewJSONEncoder(encoderCfg),
	zapcore.Lock(os.Stdout),
	atom,
))
defer logger.Sync()

logger.Info("info logging enabled")
atom.SetLevel(zap.ErrorLevel)
logger.Info("info logging disabled")
英文:

Yes it is possible using AtomicLevel. From the docs:

atom := zap.NewAtomicLevel()

// To keep the example deterministic, disable timestamps in the output.
encoderCfg := zap.NewProductionEncoderConfig()
encoderCfg.TimeKey = ""

logger := zap.New(zapcore.NewCore(
	zapcore.NewJSONEncoder(encoderCfg),
	zapcore.Lock(os.Stdout),
	atom,
))
defer logger.Sync()

logger.Info("info logging enabled")
atom.SetLevel(zap.ErrorLevel)
logger.Info("info logging disabled")

答案2

得分: 6

更好的答案:如Oliver Dain建议的,使用zap.AtomicLevel。详细信息请参考他们的答案。

另一种选择是创建一个带有自定义LevelEnabler函数的核心。您可以使用zap.LevelEnablerFunc将闭包转换为zapcore.LevelEnabler

相关文档:

LevelEnabler决定在记录消息时是否启用给定的日志级别。

LevelEnablerFunc是使用匿名函数实现zapcore.LevelEnabler的便捷方式。

该函数可以根据运行时发生变化的其他变量返回truefalse

    // 将在运行时实际初始化和更改
    // 基于您的业务逻辑
    var infoEnabled bool 

	errorUnlessEnabled := zap.LevelEnablerFunc(func(level zapcore.Level) bool {
        // true: 在此级别记录消息
        // false: 跳过此级别的消息
		return level >= zapcore.ErrorLevel || infoEnabled
	})

	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
		os.Stdout,
		errorUnlessEnabled,
	)
    logger := zap.New(core)

    logger.Info("foo") // 不会记录
    
    infoEnabled = true

    logger.Info("foo again") // 记录

PS:此代码是人为构造的。您的程序将需要实现初始化、运行时值更改以及对infoEnabled变量的适当同步(如果需要)。

您可以在playground中运行此示例:https://play.golang.org/p/oT3nvnP1Bwc

英文:

Better answer: as suggested by @Oliver Dain, use zap.AtomicLevel. See their answer for details.

Another option is to create a core with a custom LevelEnabler function. You can use zap.LevelEnablerFunc to convert a closure to a zapcore.LevelEnabler.

The relevant docs:

> LevelEnabler decides whether a given logging level is enabled when logging a message.

> LevelEnablerFunc is a convenient way to implement zapcore.LevelEnabler with an anonymous function.

That function may then return true or false based on some other variable that changes at runtime:

    // will be actually initialized and changed at run time 
    // based on your business logic
    var infoEnabled bool 

	errorUnlessEnabled := zap.LevelEnablerFunc(func(level zapcore.Level) bool {
        // true: log message at this level
        // false: skip message at this level
		return level >= zapcore.ErrorLevel || infoEnabled
	})

	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
		os.Stdout,
		errorUnlessEnabled,
	)
    logger := zap.New(core)

    logger.Info("foo") // not logged
    
    infoEnabled = true

    logger.Info("foo again") // logged

PS: this code is contrived. Your program will have to implement initialization, value change at run-time and proper synchronization (if needed) over the infoEnabled variable.

You can run this example in the playground: https://play.golang.org/p/oT3nvnP1Bwc

huangapple
  • 本文由 发表于 2021年10月27日 19:56:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/69738222.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定