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
139
140
141
142
143
144
145
146
147
148
149
|
// Package log implements a basic level based logger
package log
import (
"fmt"
"io"
"log"
"os"
"path"
"codeberg.org/eduVPN/eduvpn-common/internal/util"
)
// FileLogger defines the type of logger that this package implements
// As the name suggests, it saves the log to a file.
type FileLogger struct {
// Level indicates which maximum level this logger actually forwards to the file
Level Level
// file represents a pointer to the open log file
file *os.File
}
// Logger is the global logger instance
var Logger *FileLogger
// Level is the level of log, e.g. debug
type Level int8
const (
// LevelNotSet indicates level not set, not allowed.
LevelNotSet Level = iota
// LevelDebug indicates that the message is not an error but is there for debugging.
LevelDebug
// LevelInfo indicates that the message is not an error but is there for additional information.
LevelInfo
// LevelWarning indicates only a warning, the app still functions.
LevelWarning
// LevelError indicates a generic error, the app still functions but some functionality might not work.
LevelError
// LevelFatal indicates a fatal error, the app cannot function correctly when such an error occurs.
LevelFatal
)
// String returns the string of each level.
func (e Level) String() string {
switch e {
case LevelNotSet:
return "NOTSET"
case LevelDebug:
return "DEBUG"
case LevelInfo:
return "INFO"
case LevelWarning:
return "WARNING"
case LevelError:
return "ERROR"
case LevelFatal:
return "FATAL"
default:
return "UNKNOWN"
}
}
// Init initializes the logger by forwarding a max level 'level' and a directory 'directory' where the log should be stored
// If the logger cannot be initialized, for example an error in opening the log file, an error is returned.
func (logger *FileLogger) Init(lvl Level, dir string) error {
err := util.EnsureDirectory(dir)
if err != nil {
return err
}
f, err := os.OpenFile(
logger.filename(dir),
os.O_RDWR|os.O_CREATE|os.O_APPEND,
0o666,
)
if err != nil {
return fmt.Errorf("failed creating log: %w", err)
}
multi := io.MultiWriter(os.Stdout, f)
log.SetOutput(multi)
logger.file = f
logger.Level = lvl
return nil
}
// Inherit logs an error with a message and params using the error level verbosity of the error.
// The message is always prefixed with the error.
func (logger *FileLogger) Inherit(err error, msg string) {
if err == nil {
return
}
s := "%s %s"
logger.Errorf(s, err.Error(), msg)
}
// Debugf logs a message with parameters as level LevelDebug.
func (logger *FileLogger) Debugf(msg string, params ...interface{}) {
logger.log(LevelDebug, msg, params...)
}
// Infof logs a message with parameters as level LevelInfo.
func (logger *FileLogger) Infof(msg string, params ...interface{}) {
logger.log(LevelInfo, msg, params...)
}
// Warningf logs a message with parameters as level LevelWarning.
func (logger *FileLogger) Warningf(msg string, params ...interface{}) {
logger.log(LevelWarning, msg, params...)
}
// Errorf logs a message with parameters as level LevelError.
func (logger *FileLogger) Errorf(msg string, params ...interface{}) {
logger.log(LevelError, msg, params...)
}
// Fatalf logs a message with parameters as level LevelFatal.
func (logger *FileLogger) Fatalf(msg string, params ...interface{}) {
logger.log(LevelFatal, msg, params...)
}
// Close closes the logger by closing the internal file.
func (logger *FileLogger) Close() error {
return logger.file.Close()
}
// filename returns the filename of the logger by returning the full path as a string.
func (logger *FileLogger) filename(directory string) string {
return path.Join(directory, "log")
}
// log logs as level 'level' a message 'msg' with parameters 'params'.
func (logger *FileLogger) log(lvl Level, msg string, params ...interface{}) {
if lvl >= logger.Level && logger.Level != LevelNotSet {
fMsg := fmt.Sprintf(msg, params...)
f := fmt.Sprintf("- Go - %s - %s", lvl.String(), fMsg)
// To log file
log.Println(f)
}
}
func init() {
Logger = &FileLogger{}
}
|