summaryrefslogtreecommitdiffstats
path: root/modules/log/flags.go
blob: cadf54fdd380e8297372213b23cd9a8fd1112a20 (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
137
138
// 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}
}