diff --git a/CHANGELOG-developer.next.asciidoc b/CHANGELOG-developer.next.asciidoc index b89bd78fbbbb..4e8e09eb5ffa 100644 --- a/CHANGELOG-developer.next.asciidoc +++ b/CHANGELOG-developer.next.asciidoc @@ -87,3 +87,4 @@ The list below covers the major changes between 7.0.0-rc2 and master only. - Add fields validation for histogram subfields. {pull}17759[17759] - Add IP* fields to `fields.yml` generator script in Filebeat. {issue}17998[17998] {pull}18256[18256] - Events intended for the Elasticsearch output can now take an `op_type` metadata field of type events.OpType or string to indicate the `op_type` to use for bulk indexing. {pull}12606[12606] +- Add `ECSEnabled()` method to logger instances. {pull}18820[18820] diff --git a/libbeat/logp/configure/logging_test.go b/libbeat/logp/configure/logging_test.go new file mode 100644 index 000000000000..7054c84c94c6 --- /dev/null +++ b/libbeat/logp/configure/logging_test.go @@ -0,0 +1,36 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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 configure + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/logp" +) + +func TestECSEnabled(t *testing.T) { + logger := logp.NewLogger("noECS") + assert.False(t, logger.ECSEnabled()) + + Logging("mockbeat", common.MustNewConfigFrom(`{"ecs":true}`)) + logger = logp.NewLogger("withECS") + assert.True(t, logger.ECSEnabled()) +} diff --git a/libbeat/logp/core.go b/libbeat/logp/core.go index e5a4f94e8ee8..08f3b9c894d4 100644 --- a/libbeat/logp/core.go +++ b/libbeat/logp/core.go @@ -48,7 +48,7 @@ func init() { selectors: map[string]struct{}{}, rootLogger: zap.NewNop(), globalLogger: zap.NewNop(), - logger: newLogger(zap.NewNop(), ""), + logger: newLogger(zap.NewNop(), "", false), //ecsEnabled=false reflects default config }) } @@ -110,7 +110,7 @@ func Configure(cfg Config) error { selectors: selectors, rootLogger: root, globalLogger: root.WithOptions(zap.AddCallerSkip(1)), - logger: newLogger(root, ""), + logger: newLogger(root, "", cfg.ECSEnabled), observedLogs: observedLogs, }) return nil diff --git a/libbeat/logp/logger.go b/libbeat/logp/logger.go index b776a6166f3c..309c8e9ca808 100644 --- a/libbeat/logp/logger.go +++ b/libbeat/logp/logger.go @@ -29,16 +29,17 @@ type LogOption = zap.Option // Logger logs messages to the configured output. type Logger struct { - logger *zap.Logger - sugar *zap.SugaredLogger + logger *zap.Logger + sugar *zap.SugaredLogger + ecsEnabled bool } -func newLogger(rootLogger *zap.Logger, selector string, options ...LogOption) *Logger { +func newLogger(rootLogger *zap.Logger, selector string, ecsEnabled bool, options ...LogOption) *Logger { log := rootLogger. WithOptions(zap.AddCallerSkip(1)). WithOptions(options...). Named(selector) - return &Logger{log, log.Sugar()} + return &Logger{log, log.Sugar(), ecsEnabled} } // NewLogger returns a new Logger labeled with the name of the selector. This @@ -47,21 +48,22 @@ func newLogger(rootLogger *zap.Logger, selector string, options ...LogOption) *L // Instead create new Logger instance that your object reuses. Or if you need to // log from a static context then you may use logp.L().Infow(), for example. func NewLogger(selector string, options ...LogOption) *Logger { - return newLogger(loadLogger().rootLogger, selector, options...) + coreLogger := loadLogger() + return newLogger(coreLogger.rootLogger, selector, coreLogger.logger.ecsEnabled, options...) } // With creates a child logger and adds structured context to it. Fields added // to the child don't affect the parent, and vice versa. func (l *Logger) With(args ...interface{}) *Logger { sugar := l.sugar.With(args...) - return &Logger{sugar.Desugar(), sugar} + return &Logger{sugar.Desugar(), sugar, l.ecsEnabled} } // Named adds a new path segment to the logger's name. Segments are joined by // periods. func (l *Logger) Named(name string) *Logger { logger := l.logger.Named(name) - return &Logger{logger, logger.Sugar()} + return &Logger{logger, logger.Sugar(), l.ecsEnabled} } // Sprint @@ -213,6 +215,13 @@ func (l *Logger) Recover(msg string) { } } +// ECSEnabled returns whether or not logging should be ECS conformant. +// Switching to ECS can mean a breaking change for the log format. +// This information is helpful until all logs are switched to ECS by default. +func (l *Logger) ECSEnabled() bool { + return l.ecsEnabled +} + // L returns an unnamed global logger. func L() *Logger { return loadLogger().logger