mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-19 07:14:22 +01:00
log-parser: move the kata-log-parser from the tests repo
to the kata-containers repo under the src/tools/log-parser folder and vendor the modules Fixes: #4100 Signed-off-by: Snir Sheriber <ssheribe@redhat.com>
This commit is contained in:
165
src/tools/log-parser/agent.go
Normal file
165
src/tools/log-parser/agent.go
Normal file
@@ -0,0 +1,165 @@
|
||||
//
|
||||
// Copyright (c) 2017-2018 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
const (
|
||||
// "source=agent" logs are actually encoded within proxy logs so need
|
||||
// to be unpacked.
|
||||
agentSourceField = "agent"
|
||||
)
|
||||
|
||||
// agentLogEntry returns true if the specified log entry actually contains
|
||||
// an encoded agent log entry.
|
||||
func agentLogEntry(le LogEntry) bool {
|
||||
if le.Source != agentSourceField && le.Source != "virtcontainers" {
|
||||
return false
|
||||
}
|
||||
|
||||
// agent v1 format
|
||||
msg := le.Msg
|
||||
if msg == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if msg == "reading guest console" {
|
||||
// v2 format - check if there is actually something on the console
|
||||
if le.Data["vmconsole"] != "" {
|
||||
return true
|
||||
}
|
||||
} else if strings.HasPrefix(msg, "time=") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// unpackAgentLogEntry unpacks the proxy log entry that encodes an agent
|
||||
// message and returns the agent log entry, discarding the proxy log entry
|
||||
// that held it.
|
||||
func unpackAgentLogEntry(le LogEntry) (agent LogEntry, err error) {
|
||||
if le.Source == agentSourceField {
|
||||
return unpackAgentLogEntry_v1(le)
|
||||
}
|
||||
if le.Msg == "reading guest console" {
|
||||
return unpackAgentLogEntry_v2(le)
|
||||
}
|
||||
|
||||
return LogEntry{}, fmt.Errorf("agent log entry not found (source: %v - msg: %v): %+v",
|
||||
le.Source, le.Msg, le)
|
||||
}
|
||||
|
||||
func unpackAgentLogEntry_v1(le LogEntry) (agent LogEntry, err error) {
|
||||
msg := le.Msg
|
||||
if msg == "" {
|
||||
return LogEntry{}, fmt.Errorf("no agent message data (entry %+v", le)
|
||||
}
|
||||
|
||||
file := le.Filename
|
||||
if file == "" {
|
||||
return LogEntry{}, fmt.Errorf("filename blank (entry %+v)", le)
|
||||
}
|
||||
|
||||
line := le.Line
|
||||
if line == 0 {
|
||||
return LogEntry{}, fmt.Errorf("invalid line number (entry %+v)", le)
|
||||
}
|
||||
|
||||
reader := strings.NewReader(le.Msg)
|
||||
|
||||
entries, err := parseLogFmtData(reader, file, false)
|
||||
if err != nil {
|
||||
return LogEntry{}, fmt.Errorf("failed to parse agent log entry %+v: %v", le, err)
|
||||
}
|
||||
|
||||
expectedRecords := 1
|
||||
|
||||
count := entries.Len()
|
||||
if count != expectedRecords {
|
||||
return LogEntry{}, fmt.Errorf("expected %d record, got %d", expectedRecords, count)
|
||||
}
|
||||
|
||||
agent = entries.Entries[0]
|
||||
|
||||
// Supplement the agent entry with a few extra details
|
||||
agent.Count = le.Count
|
||||
agent.Source = agentSourceField
|
||||
agent.Filename = file
|
||||
agent.Line = line
|
||||
|
||||
return agent, nil
|
||||
}
|
||||
|
||||
func unpackAgentLogEntry_v2(le LogEntry) (agent LogEntry, err error) {
|
||||
|
||||
agent = le
|
||||
|
||||
// we expect the agent's message to be in JSON, under le.Data["vmconsole"]
|
||||
var result map[string]string
|
||||
err = json.Unmarshal([]byte(le.Data["vmconsole"]), &result)
|
||||
if err != nil {
|
||||
// entry is not in JSON format. Use the vmconsole field as the msg, and keep the rest of the log entry unmodified
|
||||
agent.Msg = le.Data["vmconsole"]
|
||||
agent.Source = "vmconsole"
|
||||
return agent, nil
|
||||
}
|
||||
// we were able to unpack agent msg, let's drop previous Data (runtime entries)
|
||||
agent.Data = make(map[string]string)
|
||||
|
||||
for k, v := range result {
|
||||
switch k {
|
||||
case "container-id", "cid": // better to ensure consistency in the agent
|
||||
agent.Container = v
|
||||
case "level":
|
||||
switch v {
|
||||
// match agent's slog short version log leveling to the common log levels
|
||||
case "CRIT":
|
||||
agent.Level = "critical"
|
||||
case "DEBG":
|
||||
agent.Level = "debug"
|
||||
case "ERRO":
|
||||
agent.Level = "error"
|
||||
case "INFO":
|
||||
agent.Level = "info"
|
||||
case "TRCE":
|
||||
agent.Level = "trace"
|
||||
case "WARN":
|
||||
agent.Level = "warning"
|
||||
default:
|
||||
agent.Level = v
|
||||
}
|
||||
agent.Data[k] = v // in any case let's leave original level under Data
|
||||
case "msg":
|
||||
agent.Msg = v
|
||||
case "name":
|
||||
agent.Name = v
|
||||
case "pid":
|
||||
pid, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return LogEntry{}, fmt.Errorf("failed to convert pid")
|
||||
}
|
||||
agent.Pid = pid
|
||||
case "source":
|
||||
agent.Source = v
|
||||
default:
|
||||
// NOTE: we do not take the agent's timestamp into account, because there is a ~1sec delay
|
||||
// for the agent's log to get through. Using the agent's timestamp would then make its logs
|
||||
// appear out of order compared to other logs from the guest.
|
||||
// The agent's logs timestamp is still visible for reference in the Data section of the logs.
|
||||
agent.Data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return agent, nil
|
||||
}
|
||||
Reference in New Issue
Block a user