WIP: Zap core for systemd journal
Find a file
Vitaliy Potyarkin dffb3a7b31 [#18] Add CODEOWNERS
Signed-off-by: Vitaliy Potyarkin <v.potyarkin@yadro.com>
2024-12-10 19:00:44 +03:00
.gitignore [#10] Add basic templates for repository 2023-04-17 11:21:21 +03:00
.gitlint [#10] Add basic templates for repository 2023-04-17 11:21:21 +03:00
.golangci.yml [#10] Add basic templates for repository 2023-04-17 11:21:21 +03:00
.pre-commit-config.yaml [#10] Add basic templates for repository 2023-04-17 11:21:21 +03:00
benchmark_test.go [#12] Add benchmarks 2023-10-18 11:29:40 +03:00
CODEOWNERS [#18] Add CODEOWNERS 2024-12-10 19:00:44 +03:00
encoder.go [#12] Optimize field encoding a bit 2023-10-18 11:30:19 +03:00
go.mod go.mod: Tidy 2024-10-25 13:41:05 +03:00
go.sum go.mod: Tidy 2024-10-25 13:41:05 +03:00
LICENSE [#2] Add license file 2023-04-17 06:54:55 +00:00
partial_encoder.go [#15] Add partial encoder 2024-01-23 19:47:34 +03:00
partial_encoder_bench_test.go [#15] Fix partial encoder bench 2024-01-24 14:42:43 +03:00
README.md [#2] Add license file 2023-04-17 06:54:55 +00:00
syslog.go [#12] Rename file with syslog fields 2023-10-18 11:30:05 +03:00
zapjournald.go [#12] Optimize field encoding a bit 2023-10-18 11:30:19 +03:00
zapjournald_test.go [#7] Add small test for getFieldValue function 2023-04-14 11:00:07 +03:00

Zap Core for systemd Journal

Package zapjournld provides zap Core for systemd Journal. It supports structured logging.

Applications may use zap logger to write logs directly into journald and may relatively freely define additional fields, which will be indexed by journald.

Information about common and custom journald fields is available on https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html.

Example

package main

import (
	"fmt"

	"git.frostfs.info/TrueCloudLab/zapjournald"
	"go.uber.org/zap"

	"github.com/ssgreg/journald"
	"go.uber.org/zap/zapcore"
)

func main() {
	// StandardLogger
	standardLogger, _ := NewStandardLogger("info")

	standardLogger.Info("Simple log raw 1")
}

func NewStandardLogger(lvlStr string) (*zap.Logger, zap.AtomicLevel) {
	lvl, err := getLogLevel(lvlStr)
	if err != nil {
		panic(err)
	}

	zc := zap.NewProductionConfig()
	zc.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	zc.Level = zap.NewAtomicLevelAt(lvl)

	// Initialize Zap.
	encoder := zapcore.NewJSONEncoder(zc.EncoderConfig)

	core := zapjournald.NewCore(zap.NewAtomicLevelAt(lvl), encoder, &journald.Journal{}, zapjournald.SyslogFields)
	coreWithContext := core.With([]zapcore.Field{
		zapjournald.SyslogFacility(zapjournald.LogDaemon),
		zapjournald.SyslogIdentifier(),
		zapjournald.SyslogPid(),
	})
	l := zap.New(coreWithContext, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)))
	return l, zc.Level
}

func getLogLevel(lvlStr string) (zapcore.Level, error) {
	var lvl zapcore.Level
	err := lvl.UnmarshalText([]byte(lvlStr))
	if err != nil {
		return lvl, fmt.Errorf("incorrect logger level configuration %s (%v), "+
			"value should be one of %v", lvlStr, err, [...]zapcore.Level{
			zapcore.DebugLevel,
			zapcore.InfoLevel,
			zapcore.WarnLevel,
			zapcore.ErrorLevel,
			zapcore.DPanicLevel,
			zapcore.PanicLevel,
			zapcore.FatalLevel,
		})
	}
	return lvl, nil
}

Results

You can read simple view like this:

sudo journalctl -f
Apr 07 13:06:34 user ___11go_build_git_frostfs_info_TrueCloudLab_zapjournald_main[330983]: {"level":"info","ts":"2023-04-07T13:06:34.990+0300","msg":"Simple log raw 1","SYSLOG_FACILITY":3,"SYSLOG_IDENTIFIER":"___11go_build_git_frostfs_info_TrueCloudLab_zapjournald_main","SYSLOG_PID":330983}

Or you can find lines by indexed field

sudo journalctl SYSLOG_PID=76385
Apr 07 13:06:34 user ___11go_build_git_frostfs_info_TrueCloudLab_zapjournald_main[330983]: {"level":"info","ts":"2023-04-07T13:06:34.990+0300","msg":"Simple log raw 1","SYSLOG_FACILITY":3,"SYSLOG_ID>

Or you can read full-structured view like this:

sudo journalctl SYSLOG_PID=76385 --output=json-pretty
{
        "PRIORITY" : "6",
        "_SYSTEMD_OWNER_UID" : "1000",
        "_HOSTNAME" : "user",
        "__MONOTONIC_TIMESTAMP" : "153798818198",
        "_SYSTEMD_SLICE" : "user-1000.slice",
        "_GID" : "1000",
        "_AUDIT_LOGINUID" : "1000",
        "_UID" : "1000",
        "SYSLOG_IDENTIFIER" : "___11go_build_git_frostfs_info_TrueCloudLab_zapjournald_main",
        "__REALTIME_TIMESTAMP" : "1680861994990646",
        "_CAP_EFFECTIVE" : "0",
        "_COMM" : "___11go_build_g",
        "_AUDIT_SESSION" : "59",
        "__CURSOR" : "s=9e2d157a286a437ea3405618239c1e07;i=14a96;b=bc1bc632f462476abc9e3e3b0c517f6c;m=23cf1fb996;t=5f8bc2e20bc36;x=9c6c4a86b6da43c",
        "_SYSTEMD_USER_SLICE" : "app.slice",
        "_SYSTEMD_CGROUP" : "/user.slice/user-1000.slice/user@1000.service/app.slice/app-gnome-jetbrains\\x2dgoland-203587.scope",
        "MESSAGE" : "{\"level\":\"info\",\"ts\":\"2023-04-07T13:06:34.990+0300\",\"msg\":\"Simple log raw 1\",\"SYSLOG_FACILITY\":3,\"SYSLOG_IDENTIFIER\":\"___11go_build_git_frostfs_info_TrueCloudLab_zapjournal>
        "_SOURCE_REALTIME_TIMESTAMP" : "1680861994990539",
        "_MACHINE_ID" : "82be99c92deb4b36850366d7742de698",
        "SYSLOG_FACILITY" : "3",
        "_BOOT_ID" : "bc1bc632f462476abc9e3e3b0c517f6c",
        "_PID" : "330983",
        "_SELINUX_CONTEXT" : "unconfined\n",
        "_SYSTEMD_UNIT" : "user@1000.service",
        "_TRANSPORT" : "journal",
        "_SYSTEMD_INVOCATION_ID" : "ed23dc57a96340029ef8bf9956182c20",
        "_SYSTEMD_USER_UNIT" : "app-gnome-jetbrains\\x2dgoland-203587.scope",
        "SYSLOG_PID" : "330983"
}

License