1
0
Fork 0
generic/logging/journald.go

136 Zeilen
3.0 KiB
Go

//go:build !windows
// +build !windows
// This package consists of parts of github.com/rs/zerolog i copied from.
// I removed the JSON argument part and added some more relevant data (The position of the caller and the goroutine)
package logging
import (
"bytes"
"encoding/json"
"fmt"
"io"
"runtime"
"strings"
"github.com/coreos/go-systemd/v22/journal"
"github.com/fxamacker/cbor/v2"
"github.com/rs/zerolog"
)
const defaultJournalDPrio = journal.PriNotice
func binaryFmt(p []byte) bool {
if len(p) > 0 && p[0] > 0x7F {
return true
}
return false
}
// NewJournalDWriter returns a zerolog log destination
// to be used as parameter to New() calls. Writing logs
// to this writer will send the log messages to journalD
// running in this system.
func NewJournalDWriter() io.Writer {
return journalWriter{}
}
type journalWriter struct {
}
// levelToJPrio converts zerolog Level string into
// journalD's priority values. JournalD has more
// priorities than zerolog.
func levelToJPrio(zLevel string) journal.Priority {
lvl, _ := zerolog.ParseLevel(zLevel)
switch lvl {
case zerolog.TraceLevel:
return journal.PriDebug
case zerolog.DebugLevel:
return journal.PriDebug
case zerolog.InfoLevel:
return journal.PriInfo
case zerolog.WarnLevel:
return journal.PriWarning
case zerolog.ErrorLevel:
return journal.PriErr
case zerolog.FatalLevel:
return journal.PriCrit
case zerolog.PanicLevel:
return journal.PriEmerg
case zerolog.NoLevel:
return journal.PriNotice
}
return defaultJournalDPrio
}
func (w journalWriter) Write(p []byte) (n int, err error) {
var event map[string]interface{}
origPLen := len(p)
if binaryFmt(p) {
cbord := cbor.NewDecoder(bytes.NewReader(p))
err = cbord.Decode(&event)
} else {
jsond := json.NewDecoder(bytes.NewReader(p))
err = jsond.Decode(&event)
}
jPrio := defaultJournalDPrio
args := make(map[string]string)
if err != nil {
return
}
if l, ok := event[zerolog.LevelFieldName].(string); ok {
jPrio = levelToJPrio(l)
}
msg := ""
for key, value := range event {
jKey := strings.ToUpper(key)
switch key {
case zerolog.LevelFieldName, zerolog.TimestampFieldName:
continue
case zerolog.MessageFieldName:
msg, _ = value.(string)
continue
}
switch v := value.(type) {
case string:
args[jKey] = v
case json.Number:
args[jKey] = fmt.Sprint(value)
default:
b, err := zerolog.InterfaceMarshalFunc(value)
if err != nil {
args[jKey] = fmt.Sprintf("[error: %v]", err)
} else {
args[jKey] = string(b)
}
}
}
args["TID"] = fmt.Sprint(runtime.NumGoroutine())
var callers = make([]uintptr, 10)
// skipping the function itself
runtime.Callers(2, callers)
var frames = runtime.CallersFrames(callers)
var frame, next = frames.Next()
for next {
if !strings.Contains(frame.File, "rs/zerolog") {
args["CODE_FILE"] = frame.File
args["CODE_LINE"] = fmt.Sprint(frame.Line)
args["CODE_FUNC"] = frame.Function
break
}
frame, next = frames.Next()
}
err = journal.Send(msg, jPrio, args)
if err == nil {
n = origPLen
}
return
}