mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-30 22:06:11 +01:00
012a1e0497
Provide a bit more journald integration. Specifically: - support emission of printk-style log level prefixes, documented in [`sd-daemon`(3)](https://man7.org/linux/man-pages/man3/sd-daemon.3.html#DESCRIPTION), that allow journald to automatically annotate stderr log lines with their level; - add a new "journaldflags" item that is supposed to be used in place of "stdflags" when under journald to reduce log clutter (i. e. strip date/time info to avoid duplication, and use log level prefixes instead of textual log levels); - detect whether stderr and/or stdout are attached to journald by parsing `$JOURNAL_STREAM` environment variable and adjust console logger defaults accordingly. <!--start release-notes-assistant--> ## Draft release notes <!--URL:https://codeberg.org/forgejo/forgejo--> - Features - [PR](https://codeberg.org/forgejo/forgejo/pulls/2869): <!--number 2869 --><!--line 0 --><!--description bG9nOiBqb3VybmFsZCBpbnRlZ3JhdGlvbg==-->log: journald integration<!--description--> <!--end release-notes-assistant--> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2869 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org> Co-authored-by: Ivan Shapovalov <intelfx@intelfx.name> Co-committed-by: Ivan Shapovalov <intelfx@intelfx.name>
139 lines
4 KiB
Go
139 lines
4 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package log
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/modules/json"
|
|
)
|
|
|
|
// These flags define which text to prefix to each log entry generated
|
|
// by the Logger. Bits are or'ed together to control what's printed.
|
|
// There is no control over the order they appear (the order listed
|
|
// here) or the format they present (as described in the comments).
|
|
// The prefix is followed by a colon only if more than time is stated
|
|
// is specified. For example, flags Ldate | Ltime
|
|
// produce, 2009/01/23 01:23:23 message.
|
|
// The standard is:
|
|
// 2009/01/23 01:23:23 ...a/logger/c/d.go:23:runtime.Caller() [I]: message
|
|
const (
|
|
Ldate uint32 = 1 << iota // the date in the local time zone: 2009/01/23
|
|
Ltime // the time in the local time zone: 01:23:23
|
|
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
|
|
Llongfile // full file name and line number: /a/logger/c/d.go:23
|
|
Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
|
|
Lfuncname // function name of the caller: runtime.Caller()
|
|
Lshortfuncname // last part of the function name
|
|
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
|
|
Llevelinitial // Initial character of the provided level in brackets, eg. [I] for info
|
|
Llevel // Provided level in brackets [INFO]
|
|
Lgopid // the Goroutine-PID of the context
|
|
Llevelprefix // printk-style logging prefixes as documented in sd-daemon(3), used by journald
|
|
|
|
Lmedfile = Lshortfile | Llongfile // last 20 characters of the filename
|
|
LstdFlags = Ldate | Ltime | Lmedfile | Lshortfuncname | Llevelinitial // default
|
|
LjournaldFlags = Llevelprefix
|
|
)
|
|
|
|
const Ldefault = LstdFlags
|
|
|
|
type Flags struct {
|
|
defined bool
|
|
flags uint32
|
|
}
|
|
|
|
var flagFromString = map[string]uint32{
|
|
"date": Ldate,
|
|
"time": Ltime,
|
|
"microseconds": Lmicroseconds,
|
|
"longfile": Llongfile,
|
|
"shortfile": Lshortfile,
|
|
"funcname": Lfuncname,
|
|
"shortfuncname": Lshortfuncname,
|
|
"utc": LUTC,
|
|
"levelinitial": Llevelinitial,
|
|
"level": Llevel,
|
|
"levelprefix": Llevelprefix,
|
|
"gopid": Lgopid,
|
|
|
|
"medfile": Lmedfile,
|
|
"stdflags": LstdFlags,
|
|
"journaldflags": LjournaldFlags,
|
|
}
|
|
|
|
var flagComboToString = []struct {
|
|
flag uint32
|
|
name string
|
|
}{
|
|
// name with more bits comes first
|
|
{LstdFlags, "stdflags"},
|
|
{Lmedfile, "medfile"},
|
|
|
|
{Ldate, "date"},
|
|
{Ltime, "time"},
|
|
{Lmicroseconds, "microseconds"},
|
|
{Llongfile, "longfile"},
|
|
{Lshortfile, "shortfile"},
|
|
{Lfuncname, "funcname"},
|
|
{Lshortfuncname, "shortfuncname"},
|
|
{LUTC, "utc"},
|
|
{Llevelinitial, "levelinitial"},
|
|
{Llevel, "level"},
|
|
{Lgopid, "gopid"},
|
|
}
|
|
|
|
func (f Flags) Bits() uint32 {
|
|
if !f.defined {
|
|
return Ldefault
|
|
}
|
|
return f.flags
|
|
}
|
|
|
|
func (f Flags) String() string {
|
|
flags := f.Bits()
|
|
var flagNames []string
|
|
for _, it := range flagComboToString {
|
|
if flags&it.flag == it.flag {
|
|
flags &^= it.flag
|
|
flagNames = append(flagNames, it.name)
|
|
}
|
|
}
|
|
if len(flagNames) == 0 {
|
|
return "none"
|
|
}
|
|
sort.Strings(flagNames)
|
|
return strings.Join(flagNames, ",")
|
|
}
|
|
|
|
func (f *Flags) UnmarshalJSON(bytes []byte) error {
|
|
var s string
|
|
if err := json.Unmarshal(bytes, &s); err != nil {
|
|
return err
|
|
}
|
|
*f = FlagsFromString(s)
|
|
return nil
|
|
}
|
|
|
|
func (f Flags) MarshalJSON() ([]byte, error) {
|
|
return []byte(`"` + f.String() + `"`), nil
|
|
}
|
|
|
|
func FlagsFromString(from string, def ...uint32) Flags {
|
|
from = strings.TrimSpace(from)
|
|
if from == "" && len(def) > 0 {
|
|
return Flags{defined: true, flags: def[0]}
|
|
}
|
|
flags := uint32(0)
|
|
for _, flag := range strings.Split(strings.ToLower(from), ",") {
|
|
flags |= flagFromString[strings.TrimSpace(flag)]
|
|
}
|
|
return Flags{defined: true, flags: flags}
|
|
}
|
|
|
|
func FlagsFromBits(flags uint32) Flags {
|
|
return Flags{defined: true, flags: flags}
|
|
}
|