mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-08-09 03:48:05 +00:00
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>
166 lines
4.1 KiB
Go
166 lines
4.1 KiB
Go
//
|
|
// 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
|
|
}
|