blob: adf19ea34f6cc00580c5105d7720859b05978ffc [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 log
import (
"context"
"io"
"os"
)
import (
"github.com/go-logr/logr"
"github.com/go-logr/zapr"
"github.com/pkg/errors"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
kube_log_zap "sigs.k8s.io/controller-runtime/pkg/log/zap"
)
import (
logger_extensions "github.com/apache/dubbo-kubernetes/pkg/plugins/extensions/logger"
)
type LogLevel int
const (
OffLevel LogLevel = iota
InfoLevel
DebugLevel
)
func (l LogLevel) String() string {
switch l {
case OffLevel:
return "off"
case InfoLevel:
return "info"
case DebugLevel:
return "debug"
default:
return "unknown"
}
}
func ParseLogLevel(text string) (LogLevel, error) {
switch text {
case "off":
return OffLevel, nil
case "info":
return InfoLevel, nil
case "debug":
return DebugLevel, nil
default:
return OffLevel, errors.Errorf("unknown log level %q", text)
}
}
func NewLogger(level LogLevel) logr.Logger {
return NewLoggerTo(os.Stderr, level)
}
func NewLoggerWithRotation(level LogLevel, outputPath string, maxSize int, maxBackups int, maxAge int) logr.Logger {
return NewLoggerTo(&lumberjack.Logger{
Filename: outputPath,
MaxSize: maxSize,
MaxBackups: maxBackups,
MaxAge: maxAge,
}, level)
}
func NewLoggerTo(destWriter io.Writer, level LogLevel) logr.Logger {
return zapr.NewLogger(newZapLoggerTo(destWriter, level))
}
func newZapLoggerTo(destWriter io.Writer, level LogLevel, opts ...zap.Option) *zap.Logger {
var lvl zap.AtomicLevel
switch level {
case OffLevel:
return zap.NewNop()
case DebugLevel:
// The value we pass here is the most verbose level that
// will end up being emitted through the `V(level int)`
// accessor. Passing -10 ensures that levels up to `V(10)`
// will work, which seems like plenty.
lvl = zap.NewAtomicLevelAt(-10)
opts = append(opts, zap.AddStacktrace(zap.ErrorLevel))
default:
lvl = zap.NewAtomicLevelAt(zap.InfoLevel)
}
encCfg := zap.NewDevelopmentEncoderConfig()
enc := zapcore.NewConsoleEncoder(encCfg)
sink := zapcore.AddSync(destWriter)
opts = append(opts, zap.AddCallerSkip(1), zap.ErrorOutput(sink))
return zap.New(zapcore.NewCore(&kube_log_zap.KubeAwareEncoder{Encoder: enc, Verbose: level == DebugLevel}, sink, lvl)).
WithOptions(opts...)
}
// AddFieldsFromCtx will check if provided context contain tracing span and
// if the span is currently recording. If so, it will call spanLogValuesProcessor
// function if it's also present in the context. If not it will add trace_id
// and span_id to logged values. It will also add the tenant id to the logged
// values.
func AddFieldsFromCtx(
logger logr.Logger,
ctx context.Context,
extensions context.Context,
) logr.Logger {
return addSpanValuesToLogger(logger, ctx, extensions)
}
func addSpanValuesToLogger(
logger logr.Logger,
ctx context.Context,
extensions context.Context,
) logr.Logger {
if span := trace.SpanFromContext(ctx); span.IsRecording() {
if fn, ok := logger_extensions.FromSpanLogValuesProcessorContext(extensions); ok {
return logger.WithValues(fn(span)...)
}
return logger.WithValues(
"trace_id", span.SpanContext().TraceID(),
"span_id", span.SpanContext().SpanID(),
)
}
return logger
}