/
log.go
147 lines (130 loc) · 4.24 KB
/
log.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright (c) 2019 IoTeX Foundation
// This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
// or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
// This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
package log
import (
"log"
"net/http"
"os"
"sync"
"go.elastic.co/ecszap"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/pkg/errors"
)
// GlobalConfig defines the global logger configurations.
type GlobalConfig struct {
Zap *zap.Config `json:"zap" yaml:"zap"`
Trace *TraceConfig `json:"trace" yaml:"trace"`
StderrRedirectFile *string `json:"stderrRedirectFile" yaml:"stderrRedirectFile"`
RedirectStdLog bool `json:"stdLogRedirect" yaml:"stdLogRedirect"`
EcsIntegration bool `json:"ecsIntegration" yaml:"ecsIntegration"`
}
var (
_globalCfg GlobalConfig
_logMu sync.RWMutex
_logServeMux = http.NewServeMux()
_subLoggers map[string]*zap.Logger
_globalLoggerName = "global"
)
func init() {
zapCfg := zap.NewDevelopmentConfig()
zapCfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
zapCfg.Level.SetLevel(zap.InfoLevel)
l, err := zapCfg.Build()
if err != nil {
log.Println("Failed to init zap global logger, no zap log will be shown till zap is properly initialized: ", err)
return
}
_logMu.Lock()
_globalCfg.Zap = &zapCfg
_subLoggers = make(map[string]*zap.Logger)
_logMu.Unlock()
zap.ReplaceGlobals(l)
}
// L wraps zap.L().
func L() *zap.Logger { return zap.L() }
// S wraps zap.S().
func S() *zap.SugaredLogger { return zap.S() }
// Logger returns logger of the given name
func Logger(name string) *zap.Logger {
logger, ok := _subLoggers[name]
if !ok {
return L()
}
return logger
}
// InitLoggers initializes the global logger and other sub loggers.
func InitLoggers(globalCfg GlobalConfig, subCfgs map[string]GlobalConfig, opts ...zap.Option) error {
if _, exists := subCfgs[_globalLoggerName]; exists {
return errors.New("'" + _globalLoggerName + "' is a reserved name for global logger")
}
subCfgs[_globalLoggerName] = globalCfg
for name, cfg := range subCfgs {
if _, exists := _subLoggers[name]; exists {
return errors.Errorf("duplicate sub logger name: %s", name)
}
if cfg.Zap == nil {
zapCfg := zap.NewProductionConfig()
cfg.Zap = &zapCfg
} else {
cfg.Zap.EncoderConfig = zap.NewProductionEncoderConfig()
}
if globalCfg.EcsIntegration {
cfg.Zap.EncoderConfig = ecszap.ECSCompatibleEncoderConfig(cfg.Zap.EncoderConfig)
}
var cores []zapcore.Core
if cfg.StderrRedirectFile != nil {
stderrF, err := os.OpenFile(*cfg.StderrRedirectFile, os.O_WRONLY|os.O_CREATE|os.O_SYNC|os.O_APPEND, 0600)
if err != nil {
return err
}
cores = append(cores, zapcore.NewCore(
zapcore.NewJSONEncoder(cfg.Zap.EncoderConfig),
zapcore.AddSync(stderrF),
cfg.Zap.Level))
}
switch cfg.Zap.Encoding {
case "console":
consoleCfg := zap.NewDevelopmentConfig()
consoleCfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
cores = append(cores, zapcore.NewCore(
zapcore.NewConsoleEncoder(consoleCfg.EncoderConfig),
zapcore.AddSync(os.Stdout),
cfg.Zap.Level))
case "json":
cfg.Zap.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
cores = append(cores, zapcore.NewCore(
zapcore.NewJSONEncoder(cfg.Zap.EncoderConfig),
zapcore.AddSync(os.Stdout),
cfg.Zap.Level))
default:
return errors.Errorf("unknown encoding: %s", cfg.Zap.Encoding)
}
core := zapcore.NewTee(cores...)
logger := zap.New(core, opts...)
_logMu.Lock()
if name == _globalLoggerName {
_globalCfg = cfg
if cfg.RedirectStdLog {
zap.RedirectStdLog(logger)
}
zap.ReplaceGlobals(logger)
if err := initTraceLogger(logger, cfg.Trace); err != nil {
return err
}
} else {
_subLoggers[name] = logger
}
_logServeMux.HandleFunc("/"+name, cfg.Zap.Level.ServeHTTP)
_logMu.Unlock()
}
return nil
}
// RegisterLevelConfigMux registers log's level config http mux.
func RegisterLevelConfigMux(root *http.ServeMux) {
_logMu.Lock()
root.Handle("/logging/", http.StripPrefix("/logging", _logServeMux))
_logMu.Unlock()
}