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
|
package logging
import (
"fmt"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
"sync"
"time"
)
const (
CONSOLE = "console"
JOURNAL = "systemd-journald"
)
// defaultEncConfig defines the default zapcore.EncoderConfig for the logging package.
var defaultEncConfig = zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.StringDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
// Options define child loggers with their desired log level.
type Options map[string]zapcore.Level
// Logging implements access to a default logger and named child loggers.
// Log levels can be configured per named child via Options which, if not configured,
// fall back on a default log level.
// Logs either to the console or to systemd-journald.
type Logging struct {
logger *Logger
output string
verbosity zap.AtomicLevel
interval time.Duration
// coreFactory creates zapcore.Core based on the log level and the log output.
coreFactory func(zap.AtomicLevel) zapcore.Core
mu sync.Mutex
loggers map[string]*Logger
options Options
}
// NewLogging takes the name and log level for the default logger,
// output where log messages are written to,
// options having log levels for named child loggers
// and returns a new Logging.
func NewLogging(name string, level zapcore.Level, output string, options Options, interval time.Duration) (*Logging, error) {
verbosity := zap.NewAtomicLevelAt(level)
var coreFactory func(zap.AtomicLevel) zapcore.Core
switch output {
case CONSOLE:
enc := zapcore.NewConsoleEncoder(defaultEncConfig)
ws := zapcore.Lock(os.Stderr)
coreFactory = func(verbosity zap.AtomicLevel) zapcore.Core {
return zapcore.NewCore(enc, ws, verbosity)
}
case JOURNAL:
coreFactory = func(verbosity zap.AtomicLevel) zapcore.Core {
return NewJournaldCore(name, verbosity)
}
default:
return nil, invalidOutput(output)
}
logger := NewLogger(zap.New(coreFactory(verbosity)).Named(name).Sugar(), interval)
return &Logging{
logger: logger,
output: output,
verbosity: verbosity,
interval: interval,
coreFactory: coreFactory,
loggers: make(map[string]*Logger),
options: options,
},
nil
}
// GetChildLogger returns a named child logger.
// Log levels for named child loggers are obtained from the logging options and, if not found,
// set to the default log level.
func (l *Logging) GetChildLogger(name string) *Logger {
l.mu.Lock()
defer l.mu.Unlock()
if logger, ok := l.loggers[name]; ok {
return logger
}
var verbosity zap.AtomicLevel
if level, found := l.options[name]; found {
verbosity = zap.NewAtomicLevelAt(level)
} else {
verbosity = l.verbosity
}
logger := NewLogger(zap.New(l.coreFactory(verbosity)).Named(name).Sugar(), l.interval)
l.loggers[name] = logger
return logger
}
// GetLogger returns the default logger.
func (l *Logging) GetLogger() *Logger {
return l.logger
}
// AssertOutput returns an error if output is not a valid logger output.
func AssertOutput(o string) error {
if o == CONSOLE || o == JOURNAL {
return nil
}
return invalidOutput(o)
}
func invalidOutput(o string) error {
return fmt.Errorf("%s is not a valid logger output. Must be either %q or %q", o, CONSOLE, JOURNAL)
}
|