collector/logs/transforms/parser/klog.go (57 lines of code) (raw):
package parser
import (
"strings"
"github.com/Azure/adx-mon/collector/logs/types"
)
const ParserTypeKlog ParserType = "klog"
type KlogParserConfig struct{}
type KlogParser struct{}
// NewKlogParser creates a parser for the klog format.
// https://kubernetes.io/docs/concepts/cluster-administration/system-logs
func NewKlogParser(config KlogParserConfig) (*KlogParser, error) {
return &KlogParser{}, nil
}
func (p *KlogParser) Parse(log *types.Log, msg string) error {
// Trim leading/trailing whitespace
msg = strings.TrimSpace(msg)
if msg == "" {
return nil
}
// Split the log into header, message, and key-value pairs
headerEnd := strings.Index(msg, "]")
if headerEnd == -1 {
log.SetBodyValue("message", msg)
return nil // Gracefully handle unstructured logs
}
header := strings.TrimSpace(msg[:headerEnd])
messageAndPairs := strings.TrimSpace(msg[headerEnd+1:])
// Extract metadata from the header
fields := strings.Fields(header)
if len(fields) >= 4 {
log.SetBodyValue("timestamp", fields[1])
filenameAndLine := strings.Split(fields[3], ":")
if len(filenameAndLine) == 2 {
log.SetBodyValue("filename", filenameAndLine[0])
log.SetBodyValue("line_number", filenameAndLine[1])
}
}
// Extract the message and key-value pairs
messageParts := strings.SplitN(messageAndPairs, "\"", 3)
if len(messageParts) < 3 {
log.SetBodyValue("message", messageAndPairs)
return nil // Gracefully handle unstructured logs
}
log.SetBodyValue("message", messageParts[1])
// Parse key-value pairs
pairs := strings.Fields(messageParts[2])
for _, pair := range pairs {
kv := strings.SplitN(pair, "=", 2)
if len(kv) == 2 {
key := kv[0]
value := unquoteKlog(kv[1])
log.SetBodyValue(key, value)
}
}
return nil
}
// unquoteKlog removes surrounding quotes from a string, if present
func unquoteKlog(s string) string {
if len(s) >= 2 {
if (s[0] == '"' && s[len(s)-1] == '"') || (s[0] == '\'' && s[len(s)-1] == '\'') {
return s[1 : len(s)-1]
}
}
return s
}