Skip to content

Commit

Permalink
[Ingest Manager] Log level reloadable from fleet (#22690) (#22729)
Browse files Browse the repository at this point in the history
[Ingest Manager] Log level reloadable from fleet (#22690)
  • Loading branch information
michalpristas authored Dec 1, 2020
1 parent ffd3c9b commit 6cd245f
Show file tree
Hide file tree
Showing 16 changed files with 255 additions and 55 deletions.
12 changes: 6 additions & 6 deletions libbeat/logp/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func ConfigureWithOutputs(cfg Config, outputs ...zapcore.Core) error {

// Build a single output (stderr has priority if more than one are enabled).
if cfg.toObserver {
sink, observedLogs = observer.New(cfg.Level.zapLevel())
sink, observedLogs = observer.New(cfg.Level.ZapLevel())
} else {
sink, err = createLogOutput(cfg)
}
Expand Down Expand Up @@ -201,24 +201,24 @@ func makeOptions(cfg Config) []zap.Option {

func makeStderrOutput(cfg Config) (zapcore.Core, error) {
stderr := zapcore.Lock(os.Stderr)
return newCore(cfg, buildEncoder(cfg), stderr, cfg.Level.zapLevel()), nil
return newCore(cfg, buildEncoder(cfg), stderr, cfg.Level.ZapLevel()), nil
}

func makeDiscardOutput(cfg Config) (zapcore.Core, error) {
discard := zapcore.AddSync(ioutil.Discard)
return newCore(cfg, buildEncoder(cfg), discard, cfg.Level.zapLevel()), nil
return newCore(cfg, buildEncoder(cfg), discard, cfg.Level.ZapLevel()), nil
}

func makeSyslogOutput(cfg Config) (zapcore.Core, error) {
core, err := newSyslog(buildEncoder(cfg), cfg.Level.zapLevel())
core, err := newSyslog(buildEncoder(cfg), cfg.Level.ZapLevel())
if err != nil {
return nil, err
}
return wrappedCore(cfg, core), nil
}

func makeEventLogOutput(cfg Config) (zapcore.Core, error) {
core, err := newEventLog(cfg.Beat, buildEncoder(cfg), cfg.Level.zapLevel())
core, err := newEventLog(cfg.Beat, buildEncoder(cfg), cfg.Level.ZapLevel())
if err != nil {
return nil, err
}
Expand All @@ -244,7 +244,7 @@ func makeFileOutput(cfg Config) (zapcore.Core, error) {
return nil, errors.Wrap(err, "failed to create file rotator")
}

return newCore(cfg, buildEncoder(cfg), rotator, cfg.Level.zapLevel()), nil
return newCore(cfg, buildEncoder(cfg), rotator, cfg.Level.ZapLevel()), nil
}

func newCore(cfg Config, enc zapcore.Encoder, ws zapcore.WriteSyncer, enab zapcore.LevelEnabler) zapcore.Core {
Expand Down
3 changes: 2 additions & 1 deletion libbeat/logp/level.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ func (l Level) MarshalJSON() ([]byte, error) {
return nil, errors.Errorf("invalid level '%d'", l)
}

func (l Level) zapLevel() zapcore.Level {
// ZapLevel returns zap alternative to logp.Level.
func (l Level) ZapLevel() zapcore.Level {
z, found := zapLevels[l]
if found {
return z
Expand Down
1 change: 1 addition & 0 deletions x-pack/elastic-agent/CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@
- Removed `install-service.ps1` and `uninstall-service.ps1` from Windows .zip packaging {pull}21694[21694]
- Add `priority` to `AddOrUpdate` on dynamic composable input providers communication channel {pull}22352[22352]
- Ship `endpoint-security` logs to elasticsearch {pull}22526[22526]
- Log level reloadable from fleet {pull}22690[22690]
15 changes: 8 additions & 7 deletions x-pack/elastic-agent/pkg/agent/application/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type upgraderControl interface {
}

// New creates a new Agent and bootstrap the required subsystem.
func New(log *logger.Logger, pathConfigFile string, reexec reexecManager, uc upgraderControl) (Application, error) {
func New(log *logger.Logger, pathConfigFile string, reexec reexecManager, uc upgraderControl, agentInfo *info.AgentInfo) (Application, error) {
// Load configuration from disk to understand in which mode of operation
// we must start the elastic-agent, the mode of operation cannot be changed without restarting the
// elastic-agent.
Expand All @@ -44,7 +44,7 @@ func New(log *logger.Logger, pathConfigFile string, reexec reexecManager, uc upg
return nil, err
}

return createApplication(log, pathConfigFile, rawConfig, reexec, uc)
return createApplication(log, pathConfigFile, rawConfig, reexec, uc, agentInfo)
}

func createApplication(
Expand All @@ -53,6 +53,7 @@ func createApplication(
rawConfig *config.Config,
reexec reexecManager,
uc upgraderControl,
agentInfo *info.AgentInfo,
) (Application, error) {
warn.LogNotGA(log)
log.Info("Detecting execution mode")
Expand All @@ -63,16 +64,16 @@ func createApplication(
return nil, err
}

if isStandalone(cfg.Fleet) {
if IsStandalone(cfg.Fleet) {
log.Info("Agent is managed locally")
return newLocal(ctx, log, pathConfigFile, rawConfig, reexec, uc)
return newLocal(ctx, log, pathConfigFile, rawConfig, reexec, uc, agentInfo)
}

log.Info("Agent is managed by Fleet")
return newManaged(ctx, log, rawConfig, reexec)
return newManaged(ctx, log, rawConfig, reexec, agentInfo)
}

// missing of fleet.enabled: true or fleet.{access_token,kibana} will place Elastic Agent into standalone mode.
func isStandalone(cfg *configuration.FleetAgentConfig) bool {
// IsStandalone decides based on missing of fleet.enabled: true or fleet.{access_token,kibana} will place Elastic Agent into standalone mode.
func IsStandalone(cfg *configuration.FleetAgentConfig) bool {
return cfg == nil || !cfg.Enabled
}
4 changes: 2 additions & 2 deletions x-pack/elastic-agent/pkg/agent/application/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func testMgmtMode(t *testing.T) {
err := c.Unpack(&m)
require.NoError(t, err)
assert.Equal(t, false, m.Fleet.Enabled)
assert.Equal(t, true, isStandalone(m.Fleet))
assert.Equal(t, true, IsStandalone(m.Fleet))

})

Expand All @@ -80,7 +80,7 @@ func testMgmtMode(t *testing.T) {
err := c.Unpack(&m)
require.NoError(t, err)
assert.Equal(t, true, m.Fleet.Enabled)
assert.Equal(t, false, isStandalone(m.Fleet))
assert.Equal(t, false, IsStandalone(m.Fleet))
})
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package application

import (
"context"
"fmt"

"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger"
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/fleetapi"
)

// handlerSettings handles settings change coming from fleet and updates log level.
type handlerSettings struct {
log *logger.Logger
reexec reexecManager
agentInfo *info.AgentInfo
}

// Handle handles SETTINGS action.
func (h *handlerSettings) Handle(ctx context.Context, a action, acker fleetAcker) error {
h.log.Debugf("handlerUpgrade: action '%+v' received", a)
action, ok := a.(*fleetapi.ActionSettings)
if !ok {
return fmt.Errorf("invalid type, expected ActionSettings and received %T", a)
}

if !isSupportedLogLevel(action.LogLevel) {
return fmt.Errorf("invalid log level, expected debug|info|warning|error and received '%s'", action.LogLevel)
}

if err := h.agentInfo.LogLevel(action.LogLevel); err != nil {
return errors.New("failed to update log level", err)
}

if err := acker.Ack(ctx, a); err != nil {
h.log.Errorf("failed to acknowledge SETTINGS action with id '%s'", action.ActionID)
} else if err := acker.Commit(ctx); err != nil {
h.log.Errorf("failed to commit acker after acknowledging action with id '%s'", action.ActionID)
}

h.reexec.ReExec()
return nil
}

func isSupportedLogLevel(level string) bool {
return level == "error" || level == "debug" || level == "info" || level == "warning"
}
38 changes: 32 additions & 6 deletions x-pack/elastic-agent/pkg/agent/application/info/agent_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ const agentInfoKey = "agent"
// defaultAgentActionStoreFile is the file that will contains the action that can be replayed after restart.
const defaultAgentActionStoreFile = "action_store.yml"

const defaultLogLevel = "info"

type persistentAgentInfo struct {
ID string `json:"id" yaml:"id" config:"id"`
ID string `json:"id" yaml:"id" config:"id"`
LogLevel string `json:"logging.level,omitempty" yaml:"logging.level,omitempty" config:"logging.level,omitempty"`
}

type ioStore interface {
Expand All @@ -45,6 +48,25 @@ func AgentActionStoreFile() string {
return filepath.Join(paths.Home(), defaultAgentActionStoreFile)
}

// updateLogLevel updates log level and persists it to disk.
func updateLogLevel(level string) error {
ai, err := loadAgentInfo(false, defaultLogLevel)
if err != nil {
return err
}

if ai.LogLevel == level {
// no action needed
return nil
}

agentConfigFile := AgentConfigFile()
s := storage.NewDiskStore(agentConfigFile)

ai.LogLevel = level
return updateAgentInfo(s, ai)
}

func generateAgentID() (string, error) {
uid, err := uuid.NewV4()
if err != nil {
Expand All @@ -54,11 +76,11 @@ func generateAgentID() (string, error) {
return uid.String(), nil
}

func loadAgentInfo(forceUpdate bool) (*persistentAgentInfo, error) {
func loadAgentInfo(forceUpdate bool, logLevel string) (*persistentAgentInfo, error) {
agentConfigFile := AgentConfigFile()
s := storage.NewDiskStore(agentConfigFile)

agentinfo, err := getInfoFromStore(s)
agentinfo, err := getInfoFromStore(s, logLevel)
if err != nil {
return nil, err
}
Expand All @@ -79,7 +101,7 @@ func loadAgentInfo(forceUpdate bool) (*persistentAgentInfo, error) {
return agentinfo, nil
}

func getInfoFromStore(s ioStore) (*persistentAgentInfo, error) {
func getInfoFromStore(s ioStore, logLevel string) (*persistentAgentInfo, error) {
agentConfigFile := AgentConfigFile()
reader, err := s.Load()
if err != nil {
Expand All @@ -104,15 +126,19 @@ func getInfoFromStore(s ioStore) (*persistentAgentInfo, error) {

agentInfoSubMap, found := configMap[agentInfoKey]
if !found {
return &persistentAgentInfo{}, nil
return &persistentAgentInfo{
LogLevel: logLevel,
}, nil
}

cc, err := config.NewConfigFrom(agentInfoSubMap)
if err != nil {
return nil, errors.New(err, "failed to create config from agent info submap")
}

pid := &persistentAgentInfo{}
pid := &persistentAgentInfo{
LogLevel: logLevel,
}
if err := cc.Unpack(&pid); err != nil {
return nil, errors.New(err, "failed to unpack stored config to map")
}
Expand Down
41 changes: 25 additions & 16 deletions x-pack/elastic-agent/pkg/agent/application/info/agent_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,51 @@

package info

import "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/release"
import (
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/release"
)

// AgentInfo is a collection of information about agent.
type AgentInfo struct {
agentID string
agentID string
logLevel string
}

// NewAgentInfo creates a new agent information.
// NewAgentInfoWithLog creates a new agent information.
// In case when agent ID was already created it returns,
// this created ID otherwise it generates
// new unique identifier for agent.
// If agent config file does not exist it gets created.
func NewAgentInfo() (*AgentInfo, error) {
agentInfo, err := loadAgentInfo(false)
// Initiates log level to predefined value.
func NewAgentInfoWithLog(level string) (*AgentInfo, error) {
agentInfo, err := loadAgentInfo(false, level)
if err != nil {
return nil, err
}

return &AgentInfo{
agentID: agentInfo.ID,
agentID: agentInfo.ID,
logLevel: agentInfo.LogLevel,
}, nil
}

// ForceNewAgentInfo creates a new agent information.
// Generates new unique identifier for agent regardless
// of any existing ID.
// NewAgentInfo creates a new agent information.
// In case when agent ID was already created it returns,
// this created ID otherwise it generates
// new unique identifier for agent.
// If agent config file does not exist it gets created.
func ForceNewAgentInfo() (*AgentInfo, error) {
agentInfo, err := loadAgentInfo(true)
if err != nil {
return nil, err
func NewAgentInfo() (*AgentInfo, error) {
return NewAgentInfoWithLog(defaultLogLevel)
}

// LogLevel updates log level of agent.
func (i *AgentInfo) LogLevel(level string) error {
if err := updateLogLevel(level); err != nil {
return err
}

return &AgentInfo{
agentID: agentInfo.ID,
}, nil
i.logLevel = level
return nil
}

// AgentID returns an agent identifier.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ type AgentECSMeta struct {
BuildOriginal string `json:"build.original"`
// Upgradeable is a flag specifying if it is possible for agent to be upgraded.
Upgradeable bool `json:"upgradeable"`
// LogLevel describes currently set log level.
// Possible values: "debug"|"info"|"warning"|"error"
LogLevel string `json:"log_level"`
}

// SystemECSMeta is a collection of operating system metadata in ECS compliant object form.
Expand Down Expand Up @@ -140,6 +143,7 @@ func (i *AgentInfo) ECSMetadata() (*ECSMeta, error) {
// only upgradeable if running from Agent installer and running under the
// control of the system supervisor (or built specifically with upgrading enabled)
Upgradeable: release.Upgradeable() || (install.RunningInstalled() && install.RunningUnderSupervisor()),
LogLevel: i.logLevel,
},
},
Host: &HostECSMeta{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (c *InspectConfigCmd) inspectConfig() error {
return err
}

if isStandalone(cfg.Fleet) {
if IsStandalone(cfg.Fleet) {
return printConfig(rawConfig)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (c *InspectOutputCmd) inspectOutputs(agentInfo *info.AgentInfo) error {
return err
}

if isStandalone(cfg.Fleet) {
if IsStandalone(cfg.Fleet) {
return listOutputsFromConfig(l, agentInfo, rawConfig)
}

Expand Down Expand Up @@ -119,7 +119,7 @@ func (c *InspectOutputCmd) inspectOutput(agentInfo *info.AgentInfo) error {
return err
}

if isStandalone(cfg.Fleet) {
if IsStandalone(cfg.Fleet) {
return printOutputFromConfig(l, agentInfo, c.output, c.program, rawConfig)
}

Expand Down
5 changes: 1 addition & 4 deletions x-pack/elastic-agent/pkg/agent/application/local_mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func newLocal(
rawConfig *config.Config,
reexec reexecManager,
uc upgraderControl,
agentInfo *info.AgentInfo,
) (*Local, error) {
cfg, err := configuration.NewFromConfig(rawConfig)
if err != nil {
Expand All @@ -75,10 +76,6 @@ func newLocal(
return nil, err
}
}
agentInfo, err := info.NewAgentInfo()
if err != nil {
return nil, err
}

logR := logreporter.NewReporter(log)

Expand Down
Loading

0 comments on commit 6cd245f

Please sign in to comment.