summaryrefslogtreecommitdiff
path: root/internal/log/log.go
blob: 43bc7376aa73b47731fcfffd7534f8ae384b49d7 (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
139
140
141
142
143
144
145
146
147
148
149
150
151
// Package log implements a basic level based logger
package log

import (
	"fmt"
	"io"
	"log"
	"os"
	"path"

	"github.com/eduvpn/eduvpn-common/internal/util"
	"github.com/eduvpn/eduvpn-common/types"
)

// 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
}

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(level Level, directory string) error {
	errorMessage := "failed creating log"

	configDirErr := util.EnsureDirectory(directory)
	if configDirErr != nil {
		return types.NewWrappedError(errorMessage, configDirErr)
	}
	logFile, logOpenErr := os.OpenFile(
		logger.filename(directory),
		os.O_RDWR|os.O_CREATE|os.O_APPEND,
		0o666,
	)
	if logOpenErr != nil {
		return types.NewWrappedError(errorMessage, logOpenErr)
	}
	multi := io.MultiWriter(os.Stdout, logFile)
	log.SetOutput(multi)
	logger.file = logFile
	logger.Level = level
	return nil
}

// Inherit logs an error with a label using the error level of the error.
func (logger *FileLogger) Inherit(label string, err error) {
	level := types.ErrorLevel(err)

	msg := fmt.Sprintf("%s with err: %s", label, types.ErrorTraceback(err))
	switch level {
	case types.ErrInfo:
		logger.Info(msg)
	case types.ErrWarning:
		logger.Warning(msg)
	case types.ErrOther:
		logger.Error(msg)
	case types.ErrFatal:
		logger.Fatal(msg)
	}
}

// Debug logs a message with parameters as level LevelDebug.
func (logger *FileLogger) Debug(msg string, params ...interface{}) {
	logger.log(LevelDebug, msg, params...)
}

// Debug logs a message with parameters as level LevelInfo.
func (logger *FileLogger) Info(msg string, params ...interface{}) {
	logger.log(LevelInfo, msg, params...)
}

// Debug logs a message with parameters as level LevelWarning.
func (logger *FileLogger) Warning(msg string, params ...interface{}) {
	logger.log(LevelWarning, msg, params...)
}

// Debug logs a message with parameters as level LevelError.
func (logger *FileLogger) Error(msg string, params ...interface{}) {
	logger.log(LevelError, msg, params...)
}

// Debug logs a message with parameters as level LevelFatal.
func (logger *FileLogger) Fatal(msg string, params ...interface{}) {
	logger.log(LevelFatal, msg, params...)
}

// Close closes the logger by closing the internal file.
func (logger *FileLogger) Close() {
	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(level Level, msg string, params ...interface{}) {
	if level >= logger.Level && logger.Level != LevelNotSet {
		formattedMsg := fmt.Sprintf(msg, params...)
		format := fmt.Sprintf("- Go - %s - %s", level.String(), formattedMsg)
		// To log file
		log.Println(format)
	}
}