Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elastic-Agent duplicates log.level from its sub components if they're not compatible with zap. #4559

Open
belimawr opened this issue Apr 10, 2024 · 2 comments
Labels
bug Something isn't working Team:Elastic-Agent Label for the Agent team

Comments

@belimawr
Copy link
Contributor

When the Elastic-Agent reads a line written to stderr or stdout by a process it's running, it tries to parse it as JSON, then it tries to extract the log level from the key log.level. The value is then passed to UnmarshalText from zapcore.Level. If this call returns an error, the log.level key is left in the JSON and when the entry is logged the Elastic-Agent logger for that process will also add it's own log.level key, ending on a duplication.

The JSON is handled here

func (r *logWriter) handleJSON(line string) bool {
var evt map[string]interface{}
if err := json.Unmarshal([]byte(line), &evt); err != nil {
return false
}
lvl := getLevel(evt, r.logCfg.LevelKey)
ts := getTimestamp(evt, r.logCfg.TimeKey, r.logCfg.TimeFormat)
msg := getMessage(evt, r.logCfg.MessageKey)
fields := getFields(evt, r.logCfg.IgnoreKeys)
allowedLvl := r.logLevel.Level()
unitId := getUnitId(evt)
if unitId != "" {
if r.unitLevels != nil {
if unitLevel, ok := r.unitLevels[unitId]; ok {
allowedLvl = unitLevel
}
}
}
if allowedLvl.Enabled(lvl) {
_ = r.loggerCore.Write(zapcore.Entry{
Level: lvl,
Time: ts,
Message: msg,
}, fields)
}
return true
}
then the level is parsed by getLevel
func getLevel(evt map[string]interface{}, key string) zapcore.Level {
lvl := zapcore.InfoLevel
err := unmarshalLevel(&lvl, getStrVal(evt, key))
if err == nil {
delete(evt, key)
}
return lvl
}
that calls unmarshalLevel
func unmarshalLevel(lvl *zapcore.Level, val string) error {
if val == "" {
return errors.New("empty val")
} else if val == "trace" {
// zap doesn't handle trace level we cast to debug
*lvl = zapcore.DebugLevel
return nil
}
return lvl.UnmarshalText([]byte(val))
}
which in turn delegates to zapcore.Level.UnmarshalText
return lvl.UnmarshalText([]byte(val))

If the parsing errors, then the log.level is not removed from the JSON

if err == nil {
delete(evt, key)
}
which leads to the duplication of log.level as zap will always add it to the final log entry.

@belimawr belimawr added bug Something isn't working Team:Elastic-Agent Label for the Agent team labels Apr 10, 2024
@elasticmachine
Copy link
Contributor

Pinging @elastic/elastic-agent (Team:Elastic-Agent)

@florianl
Copy link
Member

florianl commented Jun 6, 2024

Compatibility of zap increased with the merge of uber-go/zap#1429.
Previously, zap was not able to handle log.level if the level indicator was warning. A popular logging package, that uses warning instead of warn is sirupsen/logrus.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working Team:Elastic-Agent Label for the Agent team
Projects
None yet
Development

No branches or pull requests

3 participants