mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-11-04 00:11:04 +00:00 
			
		
		
		
	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>
		
			
				
	
	
		
			136 lines
		
	
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
						|
// SPDX-License-Identifier: MIT
 | 
						|
 | 
						|
package log
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"code.gitea.io/gitea/modules/json"
 | 
						|
)
 | 
						|
 | 
						|
// Level is the level of the logger
 | 
						|
type Level int
 | 
						|
 | 
						|
const (
 | 
						|
	UNDEFINED Level = iota
 | 
						|
	TRACE
 | 
						|
	DEBUG
 | 
						|
	INFO
 | 
						|
	WARN
 | 
						|
	ERROR
 | 
						|
	FATAL
 | 
						|
	NONE
 | 
						|
)
 | 
						|
 | 
						|
const CRITICAL = ERROR // most logger frameworks doesn't support CRITICAL, and it doesn't seem useful
 | 
						|
 | 
						|
var toString = map[Level]string{
 | 
						|
	UNDEFINED: "undefined",
 | 
						|
 | 
						|
	TRACE: "trace",
 | 
						|
	DEBUG: "debug",
 | 
						|
	INFO:  "info",
 | 
						|
	WARN:  "warn",
 | 
						|
	ERROR: "error",
 | 
						|
 | 
						|
	FATAL: "fatal",
 | 
						|
	NONE:  "none",
 | 
						|
}
 | 
						|
 | 
						|
// Machine-readable log level prefixes as defined in sd-daemon(3).
 | 
						|
//
 | 
						|
// "If a systemd service definition file is configured with StandardError=journal
 | 
						|
// or StandardError=kmsg (and similar with StandardOutput=), these prefixes can
 | 
						|
// be used to encode a log level in lines printed. <...> To use these prefixes
 | 
						|
// simply prefix every line with one of these strings. A line that is not prefixed
 | 
						|
// will be logged at the default log level SD_INFO."
 | 
						|
var toJournalPrefix = map[Level]string{
 | 
						|
	TRACE: "<7>", // SD_DEBUG
 | 
						|
	DEBUG: "<6>", // SD_INFO
 | 
						|
	INFO:  "<5>", // SD_NOTICE
 | 
						|
	WARN:  "<4>", // SD_WARNING
 | 
						|
	ERROR: "<3>", // SD_ERR
 | 
						|
	FATAL: "<2>", // SD_CRIT
 | 
						|
}
 | 
						|
 | 
						|
var toLevel = map[string]Level{
 | 
						|
	"undefined": UNDEFINED,
 | 
						|
 | 
						|
	"trace":   TRACE,
 | 
						|
	"debug":   DEBUG,
 | 
						|
	"info":    INFO,
 | 
						|
	"warn":    WARN,
 | 
						|
	"warning": WARN,
 | 
						|
	"error":   ERROR,
 | 
						|
 | 
						|
	"fatal": FATAL,
 | 
						|
	"none":  NONE,
 | 
						|
}
 | 
						|
 | 
						|
var levelToColor = map[Level][]ColorAttribute{
 | 
						|
	TRACE: {Bold, FgCyan},
 | 
						|
	DEBUG: {Bold, FgBlue},
 | 
						|
	INFO:  {Bold, FgGreen},
 | 
						|
	WARN:  {Bold, FgYellow},
 | 
						|
	ERROR: {Bold, FgRed},
 | 
						|
	FATAL: {Bold, BgRed},
 | 
						|
	NONE:  {Reset},
 | 
						|
}
 | 
						|
 | 
						|
func (l Level) String() string {
 | 
						|
	s, ok := toString[l]
 | 
						|
	if ok {
 | 
						|
		return s
 | 
						|
	}
 | 
						|
	return "info"
 | 
						|
}
 | 
						|
 | 
						|
func (l Level) JournalPrefix() string {
 | 
						|
	return toJournalPrefix[l]
 | 
						|
}
 | 
						|
 | 
						|
func (l Level) ColorAttributes() []ColorAttribute {
 | 
						|
	color, ok := levelToColor[l]
 | 
						|
	if ok {
 | 
						|
		return color
 | 
						|
	}
 | 
						|
	none := levelToColor[NONE]
 | 
						|
	return none
 | 
						|
}
 | 
						|
 | 
						|
// MarshalJSON takes a Level and turns it into text
 | 
						|
func (l Level) MarshalJSON() ([]byte, error) {
 | 
						|
	buffer := bytes.NewBufferString(`"`)
 | 
						|
	buffer.WriteString(toString[l])
 | 
						|
	buffer.WriteString(`"`)
 | 
						|
	return buffer.Bytes(), nil
 | 
						|
}
 | 
						|
 | 
						|
// UnmarshalJSON takes text and turns it into a Level
 | 
						|
func (l *Level) UnmarshalJSON(b []byte) error {
 | 
						|
	var tmp any
 | 
						|
	err := json.Unmarshal(b, &tmp)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	switch v := tmp.(type) {
 | 
						|
	case string:
 | 
						|
		*l = LevelFromString(v)
 | 
						|
	case int:
 | 
						|
		*l = LevelFromString(Level(v).String())
 | 
						|
	default:
 | 
						|
		*l = INFO
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// LevelFromString takes a level string and returns a Level
 | 
						|
func LevelFromString(level string) Level {
 | 
						|
	if l, ok := toLevel[strings.ToLower(level)]; ok {
 | 
						|
		return l
 | 
						|
	}
 | 
						|
	return INFO
 | 
						|
}
 |