summaryrefslogtreecommitdiffstats
path: root/pkg/logging/journald_core.go
blob: 50dd39f80c934bf017a8b3d7645e5b5e1c05d6e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package logging

import (
	"github.com/icinga/icingadb/pkg/utils"
	"github.com/pkg/errors"
	"github.com/ssgreg/journald"
	"go.uber.org/zap/zapcore"
	"strings"
	"unicode"
)

// priorities maps zapcore.Level to journal.Priority.
var priorities = map[zapcore.Level]journald.Priority{
	zapcore.DebugLevel:  journald.PriorityDebug,
	zapcore.InfoLevel:   journald.PriorityInfo,
	zapcore.WarnLevel:   journald.PriorityWarning,
	zapcore.ErrorLevel:  journald.PriorityErr,
	zapcore.FatalLevel:  journald.PriorityCrit,
	zapcore.PanicLevel:  journald.PriorityCrit,
	zapcore.DPanicLevel: journald.PriorityCrit,
}

// NewJournaldCore returns a zapcore.Core that sends log entries to systemd-journald and
// uses the given identifier as a prefix for structured logging context that is sent as journal fields.
func NewJournaldCore(identifier string, enab zapcore.LevelEnabler) zapcore.Core {
	return &journaldCore{
		LevelEnabler: enab,
		identifier:   identifier,
		identifierU:  strings.ToUpper(identifier),
	}
}

type journaldCore struct {
	zapcore.LevelEnabler
	context     []zapcore.Field
	identifier  string
	identifierU string
}

func (c *journaldCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
	if c.Enabled(ent.Level) {
		return ce.AddCore(ent, c)
	}

	return ce
}

func (c *journaldCore) Sync() error {
	return nil
}

func (c *journaldCore) With(fields []zapcore.Field) zapcore.Core {
	cc := *c
	cc.context = append(cc.context[:len(cc.context):len(cc.context)], fields...)

	return &cc
}

func (c *journaldCore) Write(ent zapcore.Entry, fields []zapcore.Field) error {
	pri, ok := priorities[ent.Level]
	if !ok {
		return errors.Errorf("unknown log level %q", ent.Level)
	}

	enc := zapcore.NewMapObjectEncoder()
	c.addFields(enc, fields)
	c.addFields(enc, c.context)
	enc.Fields["SYSLOG_IDENTIFIER"] = c.identifier

	message := ent.Message
	if ent.LoggerName != c.identifier {
		message = ent.LoggerName + ": " + message
	}

	return journald.Send(message, pri, enc.Fields)
}

func (c *journaldCore) addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) {
	for _, field := range fields {
		field.Key = c.identifierU +
			"_" +
			utils.ConvertCamelCase(field.Key, unicode.UpperCase, '_')
		field.AddTo(enc)
	}
}