log.go
package xlog
import (
"context"
"fmt"
"os"
"runtime/debug"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
)
const (
DefaultLinkName = "latest.log"
DefaultFileMaxAge = 7 * 86400
DefaultFileRotationTime = 86400
DefaultTimeFormat = "2006-01-02T15:04:05Z07:00"
DefaultTextEncoder = "json"
DefaultMessageKey = "msg"
DefaultLevelKey = "log.level"
DefaultTimeKey = "@timestamp"
DefaultNameKey = "log.logger"
DefaultCallerKey = "log.caller"
DefaultStacktraceKey = "stacktrace"
DefaultServiceIdKey = "service.id"
DefaultTraceKey = "traceID"
)
const (
// DebugLevel logs are typically voluminous, and are usually disabled in
// production.
DebugLevel = "debug"
// InfoLevel is the default logging priority.
InfoLevel = "info"
// WarnLevel logs are more important than Info, but don't need individual
// human review.
WarnLevel = "warn"
// ErrorLevel logs are high-priority. If an application is running smoothly,
// it shouldn't generate any error-level logs.
ErrorLevel = "error"
// DPanicLevel logs are particularly important errors. In development the
// logger panics after writing the message.
DPanicLevel = "dpanic"
// PanicLevel logs a message, then panics.
PanicLevel = "panic"
// FatalLevel logs a message, then calls os.Exit(1).
FatalLevel = "fatal"
// LowercaseLevelEncoder serializes a Level to a lowercase string. For example,
// InfoLevel is serialized to "info".
LowercaseLevelEncoder = "LowercaseLevelEncoder"
// LowercaseColorLevelEncoder serializes a Level to a lowercase string and adds coloring.
// For example, InfoLevel is serialized to "info" and colored blue.
LowercaseColorLevelEncoder = "LowercaseColorLevelEncoder"
// CapitalLevelEncoder serializes a Level to an all-caps string. For example,
// InfoLevel is serialized to "INFO".
CapitalLevelEncoder = "CapitalLevelEncoder"
// CapitalColorLevelEncoder serializes a Level to an all-caps string and adds color.
// For example, InfoLevel is serialized to "INFO" and colored blue.
CapitalColorLevelEncoder = "CapitalColorLevelEncoder"
)
// Field is an alias for Field.
// Aliasing this type dramatically improves the navigability of this package's API documentation.
type Field = zap.Field
var (
// Skip constructs a no-op field, which is often useful when handling invalid inputs in other Field constructors
Skip = zap.Skip
// Binary constructs a field that carries an opaque binary blob.
// Binary data is serialized in an encoding-appropriate format.
// For example, zap's JSON encoder base64-encodes binary blobs.
// To log UTF-8 encoded text, use ByteString.
Binary = zap.Binary
// Bool constructs a field that carries a bool.
Bool = zap.Bool
// Boolp constructs a field that carries a *bool.
// The returned Field will safely and explicitly represent `nil` when appropriate.
Boolp = zap.Boolp
// ByteString constructs a field that carries UTF-8 encoded text as a []byte.
// To log opaque binary blobs (which aren't necessarily valid UTF-8), use Binary.
ByteString = zap.ByteString
// ByteStrings constructs a field that carries a slice of []byte, each of which
// must be UTF-8 encoded text.
ByteStrings = zap.ByteStrings
// Complex128 constructs a field that carries a complex number.
// Unlike most numeric fields, this costs an allocation (to convert the complex128 to interface{}).
Complex128 = zap.Complex128
// Complex128p constructs a field that carries a *complex128.
// The returned Field will safely and explicitly represent `nil` when appropriate.
Complex128p = zap.Complex128p
// Complex64 constructs a field that carries a complex number.
// Unlike most numeric fields, this costs an allocation (to convert the complex64 to interface{}).
Complex64 = zap.Complex64
// Complex64p constructs a field that carries a *complex64.
// The returned Field will safely and explicitly represent `nil` when appropriate.
Complex64p = zap.Complex64p
// Float64 constructs a field that carries a float64.
// The way the floating-point value is represented is encoder-dependent, so marshaling is necessarily lazy.
Float64 = zap.Float64
// Float64p constructs a field that carries a *float64. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Float64p = zap.Float64p
// Float32 constructs a field that carries a float32. The way the
// floating-point value is represented is encoder-dependent, so marshaling is
// necessarily lazy.
Float32 = zap.Float32
// Float32p constructs a field that carries a *float32. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Float32p = zap.Float32p
// Int constructs a field with the given key and value.
Int = zap.Int
// Intp constructs a field that carries a *int. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Intp = zap.Intp
// Int64 constructs a field with the given key and value.
Int64 = zap.Int64
// Int64p constructs a field that carries a *int64. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Int64p = zap.Int64p
// Int32 constructs a field with the given key and value.
Int32 = zap.Int32
// Int32p constructs a field that carries a *int32. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Int32p = zap.Int32p
// Int16 constructs a field with the given key and value.
Int16 = zap.Int16
// Int16p constructs a field that carries a *int16. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Int16p = zap.Int16p
// Int8 constructs a field with the given key and value.
Int8 = zap.Int8
// Int8p constructs a field that carries a *int8. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Int8p = zap.Int8p
// String constructs a field with the given key and value.
String = zap.String
// Stringp constructs a field that carries a *string. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Stringp = zap.Stringp
// Uint constructs a field with the given key and value.
Uint = zap.Uint
// Uintp constructs a field that carries a *uint. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Uintp = zap.Uintp
// Uint64 constructs a field with the given key and value.
Uint64 = zap.Uint64
// Uint64p constructs a field that carries a *uint64. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Uint64p = zap.Uint64p
// Uint32 constructs a field with the given key and value.
Uint32 = zap.Uint32
// Uint32p constructs a field that carries a *uint32. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Uint32p = zap.Uint32p
// Uint16 constructs a field with the given key and value.
Uint16 = zap.Uint16
// Uint16p constructs a field that carries a *uint16. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Uint16p = zap.Uint16p
// Uint8 constructs a field with the given key and value.
Uint8 = zap.Uint8
// Uint8p constructs a field that carries a *uint8. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Uint8p = zap.Uint8p
// Uintptr constructs a field with the given key and value.
Uintptr = zap.Uintptr
// Uintptrp constructs a field that carries a *uintptr. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Uintptrp = zap.Uintptrp
// Reflect constructs a field with the given key and an arbitrary object. It uses
// an encoding-appropriate, reflection-based function to lazily serialize nearly
// any object into the logging context, but it's relatively slow and
// allocation-heavy. Outside tests, Any is always a better choice.
//
// If encoding fails (e.g., trying to serialize a map[int]string to JSON), Reflect
// includes the error message in the final log output.
Reflect = zap.Reflect
// Namespace creates a named, isolated scope within the logger's context. All
// subsequent fields will be added to the new namespace.
//
// This helps prevent key collisions when injecting loggers into sub-components
// or third-party libraries.
Namespace = zap.Namespace
// Stringer constructs a field with the given key and the output of the value's
// String method. The Stringer's String method is called lazily.
Stringer = zap.Stringer
// Time constructs a Field with the given key and value. The encoder
// controls how the time is serialized.
Time = zap.Time
// Timep constructs a field that carries a *time.Time. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Timep = zap.Timep
// Stack constructs a field that stores a stacktrace of the current goroutine
// under provided key. Keep in mind that taking a stacktrace is eager and
// expensive (relatively speaking); this function both makes an allocation and
// takes about two microseconds.
Stack = zap.Stack
// StackSkip constructs a field similarly to Stack, but also skips the given
// number of frames from the top of the stacktrace.
StackSkip = zap.StackSkip
// Duration constructs a field with the given key and value. The encoder
// controls how the duration is serialized.
Duration = zap.Duration
// Durationp constructs a field that carries a *time.Duration. The returned Field will safely
// and explicitly represent `nil` when appropriate.
Durationp = zap.Durationp
// Object constructs a field with the given key and ObjectMarshaler. It
// provides a flexible, but still type-safe and efficient, way to add map- or
// struct-like user-defined types to the logging context. The struct's
// MarshalLogObject method is called lazily.
Object = zap.Object
// Inline constructs a Field that is similar to Object, but it
// will add the elements of the provided ObjectMarshaler to the
// current namespace.
Inline = zap.Inline
// Any takes a key and an arbitrary value and chooses the best way to represent
// them as a field, falling back to a reflection-based approach only if
// necessary.
//
// Since byte/uint8 and rune/int32 are aliases, Any can't differentiate between
// them. To minimize surprises, []byte values are treated as binary blobs, byte
// values are treated as uint8, and runes are always treated as integers.
Any = zap.Any
)
var id, _ = os.Hostname()
type Config struct {
Path string `json:"path" toml:"path"`
FileFormat string `json:"fileFormat" toml:"fileFormat"`
Level string `json:"level" toml:"level"`
TimeZone string `json:"timezone" toml:"timezone"`
FilePrefix string `json:"filePrefix" toml:"filePrefix"`
TextEncoder string `json:"textEncoder" toml:"textEncoder"`
ShowLine bool `json:"showLine" toml:"showLine"`
// Configure the primitive representations of common complex types. For
// example, some users may want all time.Times serialized as floating-point
// seconds since epoch, while others may prefer ISO8601 strings.
EncodeLevel string `json:"encodeLevel" toml:"encodeLevel"`
StacktraceKey string `json:"stacktraceKey" toml:"stacktraceKey"`
LogInConsole bool `json:"logInConsole" toml:"logInConsole"`
TimeFormat string `json:"timeFormat" toml:"timeFormat"`
FileMaxAge int64 `json:"fileMaxAge" toml:"fileMaxAge"`
FileRotationTime int64 `json:"fileRotationTime" toml:"fileRotationTime"`
// Set the keys used for each log entry. If any key is empty, that portion
// of the entry is omitted.
MessageKey string `json:"messageKey" toml:"messageKey"`
LevelKey string `json:"levelKey" toml:"levelKey"`
TimeKey string `json:"timeKey" toml:"timeKey"`
NameKey string `json:"nameKey" toml:"nameKey"`
CallerKey string `json:"callerKey" toml:"callerKey"`
level zapcore.Level
timezone *time.Location
}
func (c *Config) Copy() *Config {
config := *c
return &config
}
func (c *Config) initOptions() error {
if c.TimeZone != "" {
loc, err := time.LoadLocation(c.TimeZone)
if err != nil {
return fmt.Errorf("Fail to loadLocation: %s, err: %v.\n", c.TimeZone, err)
}
c.timezone = loc
} else {
c.timezone = time.UTC
}
if c.FileMaxAge == 0 {
c.FileMaxAge = DefaultFileMaxAge
}
if c.FileRotationTime == 0 {
c.FileRotationTime = DefaultFileRotationTime
}
if c.TimeFormat == "" {
c.TimeFormat = DefaultTimeFormat
}
if c.MessageKey == "" {
c.MessageKey = DefaultMessageKey
}
if c.LevelKey == "" {
c.LevelKey = DefaultLevelKey
}
if c.TimeKey == "" {
c.TimeKey = DefaultTimeKey
}
if c.NameKey == "" {
c.NameKey = DefaultNameKey
}
if c.CallerKey == "" {
c.CallerKey = DefaultCallerKey
}
if c.StacktraceKey == "" {
c.StacktraceKey = DefaultStacktraceKey
}
if c.TextEncoder == "" {
c.TextEncoder = DefaultTextEncoder
}
switch c.Level {
case DebugLevel:
c.level = zap.DebugLevel
case InfoLevel:
c.level = zap.InfoLevel
case WarnLevel:
c.level = zap.WarnLevel
case ErrorLevel:
c.level = zap.ErrorLevel
case DPanicLevel:
c.level = zap.DPanicLevel
case PanicLevel:
c.level = zap.PanicLevel
case FatalLevel:
c.level = zap.FatalLevel
default:
c.level = zap.InfoLevel
}
return nil
}
type logger struct {
c *Config
zapLogger *zap.Logger
prefix []Field
}
var xlogger *logger
func init() {
defaultConfig := &Config{}
_ = defaultConfig.initOptions()
Init(defaultConfig)
}
// Init init xlog.
func Init(c *Config) {
err := c.initOptions()
if err != nil {
panic(fmt.Errorf("fail to init log config, err: %v", err))
}
var zapLogger *zap.Logger
if c.level == zap.DebugLevel || c.level == zap.ErrorLevel {
zapLogger = zap.New(getEncoderCore(c), zap.AddStacktrace(c.level))
} else {
zapLogger = zap.New(getEncoderCore(c))
}
if c.ShowLine {
zapLogger = zapLogger.WithOptions(zap.AddCaller())
}
xlogger = &logger{c: c, zapLogger: zapLogger}
}
func Default() Logger {
return xlogger
}
func WithFields(fields ...Field) {
xlogger = Default().WithFields(fields...).(*logger)
}
// Debug logs a message at DebugLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func Debug(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, xlogger.prefix...)
xlogger.zapLogger.Debug(msg, fields...)
}
// Info logs a message at InfoLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func Info(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, xlogger.prefix...)
xlogger.zapLogger.Info(msg, fields...)
}
// Warn logs a message at WarnLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func Warn(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, xlogger.prefix...)
xlogger.zapLogger.Warn(msg, fields...)
}
// Error logs a message at ErrorLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func Error(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, ByteString("stack", debug.Stack()))
fields = append(fields, xlogger.prefix...)
xlogger.zapLogger.Error(msg, fields...)
}
// DPanic logs a message at DPanicLevel. The message includes any fields
// passed at the log site, as well as any fields accumulated on the logger.
//
// If the logger is in development mode, it then panics (DPanic means
// "development panic"). This is useful for catching errors that are
// recoverable, but shouldn't ever happen.
func DPanic(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, ByteString("stack", debug.Stack()))
fields = append(fields, xlogger.prefix...)
xlogger.zapLogger.DPanic(msg, fields...)
}
// Panic logs a message at PanicLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
//
// The logger then panics, even if logging at PanicLevel is disabled.
func Panic(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, ByteString("stack", debug.Stack()))
fields = append(fields, xlogger.prefix...)
xlogger.zapLogger.Panic(msg, fields...)
}
// Fatal logs a message at FatalLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
//
// The logger then calls os.Exit(1), even if logging at FatalLevel is
// disabled.
func Fatal(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, ByteString("stack", debug.Stack()))
fields = append(fields, xlogger.prefix...)
xlogger.zapLogger.Fatal(msg, fields...)
}
// Sync calls the underlying Core's Sync method, flushing any buffered log
// entries. Applications should take care to call Sync before exiting.
func Sync() error {
return xlogger.zapLogger.Sync()
}
type Logger interface {
WithFields(fields ...Field) Logger
Debug(ctx context.Context, msg string, fields ...Field)
Info(ctx context.Context, msg string, fields ...Field)
Warn(ctx context.Context, msg string, fields ...Field)
Error(ctx context.Context, msg string, fields ...Field)
DPanic(ctx context.Context, msg string, fields ...Field)
Panic(ctx context.Context, msg string, fields ...Field)
Fatal(ctx context.Context, msg string, fields ...Field)
Sync() error
}
// New create a new logger by zap.
func New(c *Config) Logger {
err := c.initOptions()
if err != nil {
panic(fmt.Errorf("fail to init config: %v", err))
}
var zapLogger *zap.Logger
if c.level == zap.DebugLevel || c.level == zap.ErrorLevel {
zapLogger = zap.New(getEncoderCore(c), zap.AddStacktrace(c.level))
} else {
zapLogger = zap.New(getEncoderCore(c))
}
if c.ShowLine {
zapLogger = zapLogger.WithOptions(zap.AddCaller())
}
return &logger{c: c, zapLogger: zapLogger}
}
func (l *logger) WithFields(fields ...Field) Logger {
merged := make([]Field, 0, len(l.prefix)+len(fields))
merged = append(merged, l.prefix...)
merged = append(merged, fields...)
return &logger{
c: l.c,
zapLogger: l.zapLogger,
prefix: merged,
}
}
// Debug logs a message at DebugLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func (l *logger) Debug(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, l.prefix...)
l.zapLogger.Debug(msg, fields...)
}
// Info logs a message at InfoLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func (l *logger) Info(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, l.prefix...)
l.zapLogger.Info(msg, fields...)
}
// Warn logs a message at WarnLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func (l *logger) Warn(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, l.prefix...)
l.zapLogger.Warn(msg, fields...)
}
// Error logs a message at ErrorLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
func (l *logger) Error(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, l.prefix...)
l.zapLogger.Error(msg, fields...)
}
// DPanic logs a message at DPanicLevel. The message includes any fields
// passed at the log site, as well as any fields accumulated on the logger.
//
// If the logger is in development mode, it then panics (DPanic means
// "development panic"). This is useful for catching errors that are
// recoverable, but shouldn't ever happen.
func (l *logger) DPanic(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, l.prefix...)
l.zapLogger.DPanic(msg, fields...)
}
// Panic logs a message at PanicLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
//
// The logger then panics, even if logging at PanicLevel is disabled.
func (l *logger) Panic(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, l.prefix...)
l.zapLogger.Panic(msg, fields...)
}
// Fatal logs a message at FatalLevel. The message includes any fields passed
// at the log site, as well as any fields accumulated on the logger.
//
// The logger then calls os.Exit(1), even if logging at FatalLevel is
// disabled.
func (l *logger) Fatal(ctx context.Context, msg string, fields ...Field) {
fields = initTraceField(ctx, fields)
fields = append(fields, l.prefix...)
l.zapLogger.Fatal(msg, fields...)
}
// Sync calls the underlying Core's Sync method, flushing any buffered log
// entries. Applications should take care to call Sync before exiting.
func (l *logger) Sync() error {
return l.zapLogger.Sync()
}
// getEncoderCore
func getEncoderCore(c *Config) (core zapcore.Core) {
writer, err := GetWriteSyncer(c) // add file-rotatelogs hook
if err != nil {
panic(fmt.Errorf("get Write Syncer Failed err:%v", err.Error()))
}
return zapcore.NewCore(getEncoder(c), writer, c.level)
}
// getEncoder get text encoder.
func getEncoder(c *Config) zapcore.Encoder {
if c.TextEncoder == "json" {
return zapcore.NewJSONEncoder(getEncoderConfig(c))
}
return zapcore.NewConsoleEncoder(getEncoderConfig(c))
}
// getEncoderConfig get zapcore.EncoderConfig.
func getEncoderConfig(c *Config) (encoderConfig zapcore.EncoderConfig) {
// custom time format
var CustomTimeEncoder = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format(c.TimeFormat))
}
encoderConfig = zapcore.EncoderConfig{
MessageKey: c.MessageKey,
LevelKey: c.LevelKey,
TimeKey: c.TimeKey,
NameKey: c.NameKey,
CallerKey: c.CallerKey,
StacktraceKey: c.StacktraceKey,
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: CustomTimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder,
}
switch {
case c.EncodeLevel == LowercaseLevelEncoder:
encoderConfig.EncodeLevel = zapcore.LowercaseLevelEncoder
case c.EncodeLevel == LowercaseColorLevelEncoder:
encoderConfig.EncodeLevel = zapcore.LowercaseColorLevelEncoder
case c.EncodeLevel == CapitalLevelEncoder:
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
case c.EncodeLevel == CapitalColorLevelEncoder:
encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
default:
encoderConfig.EncodeLevel = zapcore.LowercaseLevelEncoder
}
return encoderConfig
}
// GetWriteSyncer add file-rotatelogs hook
func GetWriteSyncer(c *Config) (zapcore.WriteSyncer, error) {
if c.Path != "" {
var bathLogPath, bathLinkPath string
if c.FilePrefix != "" {
bathLogPath = path.Join(c.Path, fmt.Sprintf("%v.%v", c.FilePrefix, c.FileFormat))
bathLinkPath = path.Join(c.Path, fmt.Sprintf("%v.log", c.FilePrefix))
} else {
bathLogPath = path.Join(c.Path, c.FileFormat)
bathLinkPath = path.Join(c.Path, DefaultLinkName)
}
fileWriter, err := rotatelogs.New(
bathLogPath,
rotatelogs.WithLinkName(bathLinkPath),
rotatelogs.WithMaxAge(toDuration(c.FileMaxAge)),
rotatelogs.WithRotationTime(toDuration(c.FileRotationTime)),
rotatelogs.WithLocation(c.timezone),
)
if err != nil {
return nil, err
}
if c.LogInConsole {
return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(fileWriter)), nil
}
return zapcore.AddSync(fileWriter), nil
} else {
return zapcore.AddSync(zapcore.AddSync(os.Stdout)), nil
}
}
type traceIDKey struct{}
func NewTraceIDContext(ctx context.Context, traceID string) context.Context {
return context.WithValue(ctx, traceIDKey{}, traceID)
}
func FromTraceID(ctx context.Context) (string, bool) {
v := ctx.Value(traceIDKey{})
if v != nil {
if s, ok := v.(string); ok {
return s, s != ""
}
}
return "", false
}
type CustomerIdKey struct{}
func NewCustomerIdContext(ctx context.Context, customerId int64) context.Context {
return context.WithValue(ctx, CustomerIdKey{}, customerId)
}
func FromCustomerId(ctx context.Context) (int64, bool) {
v := ctx.Value(CustomerIdKey{})
if v != nil {
if s, ok := v.(int64); ok {
return s, s != 0
}
}
return 0, false
}
func toDuration(s int64) time.Duration {
return time.Duration(s) * time.Second
}
func initTraceField(ctx context.Context, fields []Field) []Field {
traceID, ok := FromTraceID(ctx)
if ok {
fields = append(fields, String(DefaultTraceKey, traceID))
}
fields = append(fields, String(DefaultServiceIdKey, id))
return fields
}
调用 main.go
// init log
xlog.Init(conf.Conf.Log) //配置读取根据自己情况
xlog.WithFields(xlog.String("log.index", "app")) //自定义
配置toml文件,根据自己情况
[log]
path = "runtime/logs/"
fileFormat = "log.%Y%m%d.%H"
textEncoder = "json"
fileMaxAge = 604800
fileRotationTime = 3600
timezone = "UTC"
标签:...,field,fields,constructs,Field,go,日志,zap
From: https://www.cnblogs.com/qcy-blog/p/18189460