summaryrefslogtreecommitdiffstats
path: root/modules/log/level.go
blob: 47f7b83f0b0b5876cf165f4a4fcbb233cbc73fc2 (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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// 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
}