Skip to content

Commit 9f3278b

Browse files
authored
[Chore] Add functions required for single logger instance (#297)
* add functions required for local logging instances
1 parent 9948f69 commit 9f3278b

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

logp/configure/logging.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ func LoggingWithOutputs(beatName string, cfg *config.C, outputs ...zapcore.Core)
8080
}
8181

8282
// LoggingWithTypedOutputs applies some defaults then calls ConfigureWithTypedOutputs
83+
//
84+
// Deprecated: Prefer using localized loggers. Use logp.LoggingWithTypedOutputsLocal.
8385
func LoggingWithTypedOutputs(beatName string, cfg, typedCfg *config.C, logKey, kind string, outputs ...zapcore.Core) error {
8486
config := logp.DefaultConfig(environment)
8587
config.Beat = beatName
@@ -112,6 +114,39 @@ func LoggingWithTypedOutputs(beatName string, cfg, typedCfg *config.C, logKey, k
112114
return logp.ConfigureWithTypedOutput(config, typedLogpConfig, logKey, kind, outputs...)
113115
}
114116

117+
// LoggingWithTypedOutputs applies some defaults and returns a logger instance
118+
func LoggingWithTypedOutputsLocal(beatName string, cfg, typedCfg *config.C, logKey, kind string, outputs ...zapcore.Core) (*logp.Logger, error) {
119+
config := logp.DefaultConfig(environment)
120+
config.Beat = beatName
121+
if cfg != nil {
122+
if err := cfg.Unpack(&config); err != nil {
123+
return nil, err
124+
}
125+
}
126+
127+
applyFlags(&config)
128+
129+
typedLogpConfig := logp.DefaultEventConfig(environment)
130+
defaultName := typedLogpConfig.Files.Name
131+
typedLogpConfig.Beat = beatName
132+
if typedCfg != nil {
133+
if err := typedCfg.Unpack(&typedLogpConfig); err != nil {
134+
return nil, fmt.Errorf("cannot unpack typed output config: %w", err)
135+
}
136+
}
137+
138+
// Make sure we're always running on the same log level
139+
typedLogpConfig.Level = config.Level
140+
typedLogpConfig.Selectors = config.Selectors
141+
142+
// If the name has not been configured, make it {beatName}-events-data
143+
if typedLogpConfig.Files.Name == defaultName {
144+
typedLogpConfig.Files.Name = beatName + "-events-data"
145+
}
146+
147+
return logp.ConfigureWithTypedOutputLocal(config, typedLogpConfig, logKey, kind, outputs...)
148+
}
149+
115150
func applyFlags(cfg *logp.Config) {
116151
if toStderr {
117152
cfg.ToStderr = true

logp/core.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ func ConfigureWithOutputs(defaultLoggerCfg Config, outputs ...zapcore.Core) erro
157157
// only used to set selectors and level. This is useful if a part of
158158
// your code uses logp but the log output is already handled. Normal
159159
// use cases should use Configure or ConfigureWithOutput.
160+
//
161+
// Deprecated: Prefer using localized loggers. Use logp.ConfigureWithCoreLocal.
160162
func ConfigureWithCore(loggerCfg Config, core zapcore.Core) error {
161163
var (
162164
sink zapcore.Core
@@ -202,6 +204,56 @@ func ConfigureWithCore(loggerCfg Config, core zapcore.Core) error {
202204
return nil
203205
}
204206

207+
// ConfigureWithCoreLocal returns a logger using passed in
208+
// core. It is assumed that an output has already been defined with
209+
// the core and a new one should not be created. The loggerCfg is
210+
// only used to set selectors and level.
211+
func ConfigureWithCoreLocal(loggerCfg Config, core zapcore.Core) (*Logger, error) {
212+
var (
213+
sink zapcore.Core
214+
level zap.AtomicLevel
215+
)
216+
217+
level = zap.NewAtomicLevelAt(loggerCfg.Level.ZapLevel())
218+
219+
if loggerCfg.WithFields != nil {
220+
fields := make([]zapcore.Field, 0, len(loggerCfg.WithFields))
221+
for key, value := range loggerCfg.WithFields {
222+
fields = append(fields, zap.Any(key, value))
223+
}
224+
core = core.With(fields)
225+
}
226+
227+
sink = wrappedCore(core)
228+
229+
// Enabled selectors when debug is enabled.
230+
selectors := make(map[string]struct{}, len(loggerCfg.Selectors))
231+
if loggerCfg.Level.Enabled(DebugLevel) && len(loggerCfg.Selectors) > 0 {
232+
for _, sel := range loggerCfg.Selectors {
233+
selectors[strings.TrimSpace(sel)] = struct{}{}
234+
}
235+
236+
// Default to all enabled if no selectors are specified.
237+
if len(selectors) == 0 {
238+
selectors["*"] = struct{}{}
239+
}
240+
241+
sink = selectiveWrapper(sink, selectors)
242+
}
243+
244+
root := zap.New(sink, makeOptions(loggerCfg)...)
245+
// TODO: Remove this when there is no more global logger dependency
246+
storeLogger(&coreLogger{
247+
selectors: selectors,
248+
rootLogger: root,
249+
globalLogger: root.WithOptions(zap.AddCallerSkip(1)),
250+
logger: newLogger(root, ""),
251+
level: level,
252+
observedLogs: nil,
253+
})
254+
return newLogger(root, ""), nil
255+
}
256+
205257
// ConfigureWithTypedOutput configures the global logger to use typed outputs.
206258
//
207259
// If a log entry matches the defined key/value, this entry is logged using the
@@ -219,6 +271,8 @@ func ConfigureWithCore(loggerCfg Config, core zapcore.Core) error {
219271
//
220272
// If `defaultLoggerCfg.toObserver` is true, then `typedLoggerCfg` is ignored
221273
// and a single sink is used so all logs can be observed.
274+
//
275+
// Deprecated: Prefer using localized loggers. Use logp.ConfigureWithTypedOutputLocal.
222276
func ConfigureWithTypedOutput(defaultLoggerCfg, typedLoggerCfg Config, key, value string, outputs ...zapcore.Core) error {
223277
sink, level, observedLogs, selectors, err := createSink(defaultLoggerCfg, outputs...)
224278
if err != nil {
@@ -256,6 +310,57 @@ func ConfigureWithTypedOutput(defaultLoggerCfg, typedLoggerCfg Config, key, valu
256310
return nil
257311
}
258312

313+
// ConfigureWithTypedOutputLocal returns a logger that uses typed outputs.
314+
//
315+
// If a log entry matches the defined key/value, this entry is logged using the
316+
// core generated from `typedLoggerCfg`, otherwise it will be logged by all
317+
// cores in `outputs` and the one generated from `defaultLoggerCfg`.
318+
// Arguments:
319+
// - `defaultLoggerCfg` is used to create a new core that will be the default
320+
// output from the logger
321+
// - `typedLoggerCfg` is used to create a new output that will only be used
322+
// when the log entry matches `entry[logKey] = kind`
323+
// - `key` is the key the typed logger will look at
324+
// - `value` is the value compared against the `logKey` entry
325+
// - `outputs` is a list of cores that will be added together with the core
326+
// generated by `defaultLoggerCfg` as the default output for the loggger.
327+
func ConfigureWithTypedOutputLocal(defaultLoggerCfg, typedLoggerCfg Config, key, value string, outputs ...zapcore.Core) (*Logger, error) {
328+
sink, level, observedLogs, selectors, err := createSink(defaultLoggerCfg, outputs...)
329+
if err != nil {
330+
return nil, err
331+
}
332+
333+
var typedCore zapcore.Core
334+
typedCore, err = createLogOutput(typedLoggerCfg, level)
335+
336+
if err != nil {
337+
return nil, fmt.Errorf("could not create typed logger output: %w", err)
338+
}
339+
340+
sink = &typedLoggerCore{
341+
defaultCore: sink,
342+
typedCore: typedCore,
343+
key: key,
344+
value: value,
345+
}
346+
347+
sink = selectiveWrapper(sink, selectors)
348+
349+
root := zap.New(sink, makeOptions(defaultLoggerCfg)...)
350+
351+
// TODO: Remove this when there is no more global logger dependency
352+
storeLogger(&coreLogger{
353+
selectors: selectors,
354+
rootLogger: root,
355+
globalLogger: root.WithOptions(zap.AddCallerSkip(1)),
356+
logger: newLogger(root, ""),
357+
level: level,
358+
observedLogs: observedLogs,
359+
})
360+
logger := newLogger(root, "")
361+
return logger, nil
362+
}
363+
259364
func createLogOutput(cfg Config, enab zapcore.LevelEnabler) (zapcore.Core, error) {
260365
switch {
261366
case cfg.toIODiscard:
@@ -282,6 +387,8 @@ func createLogOutput(cfg Config, enab zapcore.LevelEnabler) (zapcore.Core, error
282387

283388
// DevelopmentSetup configures the logger in development mode at debug level.
284389
// By default the output goes to stderr.
390+
//
391+
// Deprecated: Prefer using localized loggers. Use logp.NewDevelopmentLogger.
285392
func DevelopmentSetup(options ...Option) error {
286393
cfg := Config{
287394
Level: DebugLevel,
@@ -297,6 +404,8 @@ func DevelopmentSetup(options ...Option) error {
297404

298405
// TestingSetup configures logging by calling DevelopmentSetup if and only if
299406
// verbose testing is enabled (as in 'go test -v').
407+
//
408+
// Deprecated: Prefer using localized loggers. Use logp.NewTestingLogger.
300409
func TestingSetup(options ...Option) error {
301410
// Use the flag to avoid a dependency on the testing package.
302411
f := flag.Lookup("test.v")

logp/logger.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,17 @@ import (
2121
"bytes"
2222
"fmt"
2323
"io"
24+
"testing"
2425

2526
"go.elastic.co/ecszap"
2627
"go.uber.org/zap"
2728
"go.uber.org/zap/zapcore"
29+
"go.uber.org/zap/zaptest"
2830
)
2931

3032
// LogOption configures a Logger.
3133
type LogOption = zap.Option
34+
type LogTestOption = zaptest.LoggerOption
3235

3336
// Logger logs messages to the configured output.
3437
type Logger struct {
@@ -53,12 +56,41 @@ func NewLogger(selector string, options ...LogOption) *Logger {
5356
return newLogger(loadLogger().rootLogger, selector, options...)
5457
}
5558

59+
// NewProductionLogger returns a production suitable logp.Logger
60+
func NewProductionLogger(selector string, options ...LogOption) (*Logger, error) {
61+
log, err := zap.NewProduction(options...)
62+
log = log.Named(selector)
63+
if err != nil {
64+
return nil, err
65+
}
66+
return &Logger{log, log.Sugar()}, nil
67+
}
68+
69+
// NewDevelopmentLogger returns a development suitable logp.Logger
70+
func NewDevelopmentLogger(selector string, options ...LogOption) (*Logger, error) {
71+
log, err := zap.NewDevelopment(options...)
72+
log = log.Named(selector)
73+
if err != nil {
74+
return nil, err
75+
}
76+
return &Logger{log, log.Sugar()}, nil
77+
}
78+
79+
// NewTestingLogger returns a testing suitable logp.Logger.
80+
func NewTestingLogger(t *testing.T, selector string, options ...LogTestOption) *Logger {
81+
log := zaptest.NewLogger(t, options...)
82+
log = log.Named(selector)
83+
return &Logger{log, log.Sugar()}
84+
}
85+
5686
// NewInMemory returns a new in-memory logger along with the buffer to which it
5787
// logs. It's goroutine safe, but operating directly on the returned buffer is not.
5888
// This logger is primary intended for short and simple use-cases such as printing
5989
// the full logs only when an operation fails.
6090
// encCfg configures the log format, use logp.ConsoleEncoderConfig for console
6191
// format, logp.JSONEncoderConfig for JSON or any other valid zapcore.EncoderConfig.
92+
//
93+
// Deprecated: Prefer using localized loggers. Use logp.NewInMemoryLocal.
6294
func NewInMemory(selector string, encCfg zapcore.EncoderConfig) (*Logger, *bytes.Buffer) {
6395
buff := bytes.Buffer{}
6496

@@ -81,6 +113,34 @@ func NewInMemory(selector string, encCfg zapcore.EncoderConfig) (*Logger, *bytes
81113
return logger, &buff
82114
}
83115

116+
// NewInMemory returns a new in-memory logger along with the buffer to which it
117+
// logs. It's goroutine safe, but operating directly on the returned buffer is not.
118+
// This logger is primary intended for short and simple use-cases such as printing
119+
// the full logs only when an operation fails.
120+
// encCfg configures the log format, use logp.ConsoleEncoderConfig for console
121+
// format, logp.JSONEncoderConfig for JSON or any other valid zapcore.EncoderConfig.
122+
func NewInMemoryLocal(selector string, encCfg zapcore.EncoderConfig) (*Logger, *bytes.Buffer) {
123+
buff := bytes.Buffer{}
124+
125+
encoderConfig := ecszap.ECSCompatibleEncoderConfig(encCfg)
126+
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
127+
encoder := zapcore.NewConsoleEncoder(encoderConfig)
128+
129+
core := zapcore.NewCore(
130+
encoder,
131+
zapcore.Lock(zapcore.AddSync(&buff)),
132+
zap.NewAtomicLevelAt(zap.DebugLevel))
133+
ecszap.ECSCompatibleEncoderConfig(ConsoleEncoderConfig())
134+
135+
logger, _ := NewDevelopmentLogger(
136+
selector,
137+
zap.WrapCore(func(in zapcore.Core) zapcore.Core {
138+
return core
139+
}))
140+
141+
return logger, &buff
142+
}
143+
84144
// WithOptions returns a clone of l with options applied.
85145
func (l *Logger) WithOptions(options ...LogOption) *Logger {
86146
cloned := l.logger.WithOptions(options...)

0 commit comments

Comments
 (0)