collector/internal/levelchanger/levelchanger.go (49 lines of code) (raw):
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package levelchanger
import (
"strings"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
type LevelChangeCondition func(entry zapcore.Entry, fields []zapcore.Field) bool
type levelChangerCore struct {
next zapcore.Core
fromLevel zapcore.Level
toLevel zapcore.Level
conditions []LevelChangeCondition
}
// This core is enabled at the requested level if the core it wraps
// is enabled at the requested level.
func (l levelChangerCore) Enabled(level zapcore.Level) bool {
return l.next.Enabled(level)
}
// This core does not allow adding additional context.
func (l levelChangerCore) With([]zapcore.Field) zapcore.Core { return l }
// This core will always add itself to the checked entry, since the Write
// method will determine whether the entry continues to the next core.
func (l levelChangerCore) Check(entry zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
return ce.AddCore(entry, l)
}
// Check if the log passes any core conditions, and if the log level matches the fromLevel,
// change the log's level to the toLevel.
func (l levelChangerCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
// Always pass if there are no conditions, otherwise check if any conditions are met.
changeLevels := len(l.conditions) == 0
for _, condition := range l.conditions {
changeLevels = changeLevels || condition(entry, fields)
}
if changeLevels && entry.Level == l.fromLevel {
entry.Level = l.toLevel
}
// Check if the next core is enabled at the (potentially) new log level.
if !l.next.Enabled(entry.Level) {
return nil
}
return l.next.Write(entry, fields)
}
// No special syncing is required for this core.
func (levelChangerCore) Sync() error { return nil }
// Create a zap option that wraps a core with a new levelChangerCore.
func NewLevelChangerOption(from, to zapcore.Level, conditions ...LevelChangeCondition) zap.Option {
return zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return levelChangerCore{
next: core,
fromLevel: from,
toLevel: to,
conditions: conditions,
}
})
}
// Make a level change condition that passes if the Entry's Caller File contains a
// substring. Can be used to change the level of all logs from some file or package.
func FilePathLevelChangeCondition(pathSubstr string) LevelChangeCondition {
return func(entry zapcore.Entry, fields []zapcore.Field) bool {
return strings.Contains(entry.Caller.File, pathSubstr)
}
}