First version of the Golang logging library
[com/golog.git] / mdclog.go
1 /*
2  *  Copyright (c) 2019 AT&T Intellectual Property.
3  *  Copyright (c) 2018-2019 Nokia.
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17
18 // Package golog implements a simple structured logging with MDC (Mapped Diagnostics Context) support.
19 package golog
20
21 import (
22         "bytes"
23         "encoding/json"
24         "fmt"
25         "io"
26         "os"
27         "sync"
28         "time"
29 )
30
31 // Level is a type define for the logging level.
32 type Level int
33
34 const (
35         // ERR is an error level log entry.
36         ERR Level = 1
37         // WARN is a warning level log entry.
38         WARN Level = 2
39         // INFO is an info level log entry.
40         INFO Level = 3
41         // DEBUG is a debug level log entry.
42         DEBUG Level = 4
43 )
44
45 // MdcLogger is the logger instance, created with InitLogger() function.
46 type MdcLogger struct {
47         proc   string
48         writer io.Writer
49         mdc    map[string]string
50         mutex  sync.Mutex
51         level  Level
52 }
53
54 type logEntry struct {
55         Ts   int64             `json:"ts"`
56         Crit string            `json:"crit"`
57         Id   string            `json:"id"`
58         Mdc  map[string]string `json:"mdc"`
59         Msg  string            `json:"msg"`
60 }
61
62 func levelString(level Level) string {
63         switch level {
64         case ERR:
65                 return "ERROR"
66         case WARN:
67                 return "WARNING"
68         case INFO:
69                 return "INFO"
70         case DEBUG:
71                 return "DEBUG"
72         default:
73                 return ""
74         }
75 }
76
77 func getTime() int64 {
78         ns := time.Time.UnixNano(time.Now())
79         return ns / int64(time.Millisecond)
80 }
81
82 func (l *MdcLogger) formatLog(level Level, msg string) ([]byte, error) {
83         log := logEntry{getTime(), levelString(level), l.proc, l.mdc, msg}
84         buf := bytes.NewBuffer(nil)
85         encoder := json.NewEncoder(buf)
86         encoder.SetEscapeHTML(false)
87         err := encoder.Encode(log)
88         return buf.Bytes(), err
89 }
90
91 func initLogger(proc string, writer io.Writer) (*MdcLogger, error) {
92         return &MdcLogger{proc: proc, writer: writer, mdc: make(map[string]string), level: DEBUG}, nil
93 }
94
95 // InitLogger is the init routine which returns a new logger instance.
96 // The program identity is given as a parameter. The identity
97 // is added to every log writing.
98 // The function returns a new instance or an error.
99 func InitLogger(proc string) (*MdcLogger, error) {
100         return initLogger(proc, os.Stdout)
101 }
102
103 // Log is the basic logging function to write a log message with
104 // the given level
105 func (l *MdcLogger) Log(level Level, formatMsg string, a ...interface{}) {
106         l.mutex.Lock()
107         defer l.mutex.Unlock()
108         if l.level < level {
109                 return
110         }
111         log, err := l.formatLog(level, fmt.Sprintf(formatMsg, a...))
112         if err == nil {
113                 l.writer.Write(log)
114         }
115 }
116
117 // Error is the "error" level logging function.
118 func (l *MdcLogger) Error(formatMsg string, a ...interface{}) {
119         l.Log(ERR, formatMsg, a...)
120 }
121
122 // Warning is the "warning" level logging function.
123 func (l *MdcLogger) Warning(formatMsg string, a ...interface{}) {
124         l.Log(WARN, formatMsg, a...)
125 }
126
127 // Info is the "info" level logging function.
128 func (l *MdcLogger) Info(formatMsg string, a ...interface{}) {
129         l.Log(INFO, formatMsg, a...)
130 }
131
132 // Debug is the "debug" level logging function.
133 func (l *MdcLogger) Debug(formatMsg string, a ...interface{}) {
134         l.Log(DEBUG, formatMsg, a...)
135 }
136
137 // LevelSet sets the current logging level.
138 // Log writings with less significant level are discarded.
139 func (l *MdcLogger) LevelSet(level Level) {
140         l.level = level
141 }
142
143 // LevelGet returns the current logging level.
144 func (l *MdcLogger) LevelGet() Level {
145         return l.level
146 }
147
148 // MdcAdd adds a new MDC key value pair to the logger.
149 func (l *MdcLogger) MdcAdd(key string, value string) {
150         l.mutex.Lock()
151         defer l.mutex.Unlock()
152         l.mdc[key] = value
153 }
154
155 // MdcRemove removes an MDC key from the logger.
156 func (l *MdcLogger) MdcRemove(key string) {
157         l.mutex.Lock()
158         defer l.mutex.Unlock()
159         delete(l.mdc, key)
160 }
161
162 // MdcGet gets the value of an MDC from the logger.
163 // The function returns the value string and a boolean
164 // which tells if the key was found or not.
165 func (l *MdcLogger) MdcGet(key string) (string, bool) {
166         l.mutex.Lock()
167         defer l.mutex.Unlock()
168         val, ok := l.mdc[key]
169         return val, ok
170 }
171
172 // MdcClean removes all MDC keys from the logger.
173 func (l *MdcLogger) MdcClean() {
174         l.mutex.Lock()
175         defer l.mutex.Unlock()
176         l.mdc = make(map[string]string)
177 }