Add new license claim
[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  * This source code is part of the near-RT RIC (RAN Intelligent Controller)
18  * platform project (RICP).
19  */
20
21 // Package golog implements a simple structured logging with MDC (Mapped Diagnostics Context) support.
22 package golog
23
24 import (
25         "bytes"
26         "encoding/json"
27         "fmt"
28         "io"
29         "os"
30         "sync"
31         "time"
32 )
33
34 // Level is a type define for the logging level.
35 type Level int
36
37 const (
38         // ERR is an error level log entry.
39         ERR Level = 1
40         // WARN is a warning level log entry.
41         WARN Level = 2
42         // INFO is an info level log entry.
43         INFO Level = 3
44         // DEBUG is a debug level log entry.
45         DEBUG Level = 4
46 )
47
48 // MdcLogger is the logger instance, created with InitLogger() function.
49 type MdcLogger struct {
50         proc   string
51         writer io.Writer
52         mdc    map[string]string
53         mutex  sync.Mutex
54         level  Level
55 }
56
57 type logEntry struct {
58         Ts   int64             `json:"ts"`
59         Crit string            `json:"crit"`
60         Id   string            `json:"id"`
61         Mdc  map[string]string `json:"mdc"`
62         Msg  string            `json:"msg"`
63 }
64
65 func levelString(level Level) string {
66         switch level {
67         case ERR:
68                 return "ERROR"
69         case WARN:
70                 return "WARNING"
71         case INFO:
72                 return "INFO"
73         case DEBUG:
74                 return "DEBUG"
75         default:
76                 return ""
77         }
78 }
79
80 func getTime() int64 {
81         ns := time.Time.UnixNano(time.Now())
82         return ns / int64(time.Millisecond)
83 }
84
85 func (l *MdcLogger) formatLog(level Level, msg string) ([]byte, error) {
86         log := logEntry{getTime(), levelString(level), l.proc, l.mdc, msg}
87         buf := bytes.NewBuffer(nil)
88         encoder := json.NewEncoder(buf)
89         encoder.SetEscapeHTML(false)
90         err := encoder.Encode(log)
91         return buf.Bytes(), err
92 }
93
94 func initLogger(proc string, writer io.Writer) (*MdcLogger, error) {
95         return &MdcLogger{proc: proc, writer: writer, mdc: make(map[string]string), level: DEBUG}, nil
96 }
97
98 // InitLogger is the init routine which returns a new logger instance.
99 // The program identity is given as a parameter. The identity
100 // is added to every log writing.
101 // The function returns a new instance or an error.
102 func InitLogger(proc string) (*MdcLogger, error) {
103         return initLogger(proc, os.Stdout)
104 }
105
106 // Log is the basic logging function to write a log message with
107 // the given level
108 func (l *MdcLogger) Log(level Level, formatMsg string, a ...interface{}) {
109         l.mutex.Lock()
110         defer l.mutex.Unlock()
111         if l.level < level {
112                 return
113         }
114         log, err := l.formatLog(level, fmt.Sprintf(formatMsg, a...))
115         if err == nil {
116                 l.writer.Write(log)
117         }
118 }
119
120 // Error is the "error" level logging function.
121 func (l *MdcLogger) Error(formatMsg string, a ...interface{}) {
122         l.Log(ERR, formatMsg, a...)
123 }
124
125 // Warning is the "warning" level logging function.
126 func (l *MdcLogger) Warning(formatMsg string, a ...interface{}) {
127         l.Log(WARN, formatMsg, a...)
128 }
129
130 // Info is the "info" level logging function.
131 func (l *MdcLogger) Info(formatMsg string, a ...interface{}) {
132         l.Log(INFO, formatMsg, a...)
133 }
134
135 // Debug is the "debug" level logging function.
136 func (l *MdcLogger) Debug(formatMsg string, a ...interface{}) {
137         l.Log(DEBUG, formatMsg, a...)
138 }
139
140 // LevelSet sets the current logging level.
141 // Log writings with less significant level are discarded.
142 func (l *MdcLogger) LevelSet(level Level) {
143         l.level = level
144 }
145
146 // LevelGet returns the current logging level.
147 func (l *MdcLogger) LevelGet() Level {
148         return l.level
149 }
150
151 // MdcAdd adds a new MDC key value pair to the logger.
152 func (l *MdcLogger) MdcAdd(key string, value string) {
153         l.mutex.Lock()
154         defer l.mutex.Unlock()
155         l.mdc[key] = value
156 }
157
158 // MdcRemove removes an MDC key from the logger.
159 func (l *MdcLogger) MdcRemove(key string) {
160         l.mutex.Lock()
161         defer l.mutex.Unlock()
162         delete(l.mdc, key)
163 }
164
165 // MdcGet gets the value of an MDC from the logger.
166 // The function returns the value string and a boolean
167 // which tells if the key was found or not.
168 func (l *MdcLogger) MdcGet(key string) (string, bool) {
169         l.mutex.Lock()
170         defer l.mutex.Unlock()
171         val, ok := l.mdc[key]
172         return val, ok
173 }
174
175 // MdcClean removes all MDC keys from the logger.
176 func (l *MdcLogger) MdcClean() {
177         l.mutex.Lock()
178         defer l.mutex.Unlock()
179         l.mdc = make(map[string]string)
180 }