-Logging library with MDC support
+Logging Library with MDC support
================================
A Golang implementation of a structured logging library with Mapped Diagnostics Context (MDC) support.
"fmt"
"os"
"time"
-
mdcloggo "gerrit.o-ran-sc.org/r/com/golog"
)
func main() {
logger, _ := mdcloggo.InitLogger("myname")
+ logFileMonitor := 0
logger.MdcAdd("foo", "bar")
logger.MdcAdd("foo2", "bar2")
+ if logger.Mdclog_format_initialize(logFileMonitor) != 0 {
+ logger.Error("Failed in MDC Log Format Initialize")
+ }
+
start := time.Now()
for i := 0; i < 10; i++ {
logger.Info("Some test logs")
\r
`{"ts":1551183682974,"crit":"INFO","id":"myprog","mdc":{"second key":"other value","mykey":"keyval"},"msg":"hello world!"}`\r
\r
+ `{"ts":1602081593063,"crit":"INFO","id":"myapp","mdc":{"PID":"21587","POD_NAME":"tta-app-5565fc4d6f-ppfl8","CONTAINER_NAME":"tta-app","SERVICE_NAME":"TEST_APP","HOST_NAME":"master-an","SYSTEM_NAME":"CloudSpace-0"},"msg":"This is an example log"}`\r
+\r
Example\r
-------\r
\r
)\r
\r
func main() {\r
+ logFileMonitor := 0;\r
logger, _ := mdcloggo.InitLogger("myname")\r
+ if(logger.Mdclog_format_initialize(logFileMonitor)!=0) {\r
+ logger.Error("UnSuccessful Format Initialize")\r
+ }\r
logger.MdcAdd("mykey", "keyval")\r
logger.Info("Some test logs")\r
} \r
.. code:: bash\r
\r
func (l *MdcLogger) MdcClean()\r
+\r
+7. Mdclog_format_initialize Adds the MDC log format with HostName, PodName, ContainerName, ServiceName,PID,CallbackNotifyforLogFieldChange\r
+\r
+.. code:: bash\r
+\r
+ func (l *MdcLogger) Mdclog_format_initialize(log_change_monitor int) (int)\r
+\r
+Description: This api Initialzes mdclog print format using MDC Array by extracting the environment variables in the calling process for "SYSTEM_NAME", "HOST_NAME", "SERVICE_NAME", "CONTAINER_NAME", "POD_NAME" & "CONFIG_MAP_NAME" mapped to HostName, ServiceName, ContainerName, Podname and Configuration-file-name of the services respectively.\r
+\r
+ Note: In K8s/Docker Containers the environment variables are declared in the Helm charts.\r
+\r
+ Refer xAPP developer guide for more information about how to define Helm chart.\r
"encoding/json"
"fmt"
"io"
+ "io/ioutil"
+ "k8s.io/utils/inotify"
"os"
+ "path/filepath"
+ "strconv"
+ "strings"
"sync"
+ "syscall"
"time"
)
// MdcLogger is the logger instance, created with InitLogger() function.
type MdcLogger struct {
- proc string
- writer io.Writer
- mdc map[string]string
- mutex sync.Mutex
- level Level
+ proc string
+ writer io.Writer
+ mdc map[string]string
+ mutex sync.Mutex
+ level Level
+ init_done int
}
type logEntry struct {
}
func initLogger(proc string, writer io.Writer) (*MdcLogger, error) {
- return &MdcLogger{proc: proc, writer: writer, mdc: make(map[string]string), level: DEBUG}, nil
+ return &MdcLogger{proc: proc, writer: writer, mdc: make(map[string]string), level: DEBUG, init_done: 0}, nil
}
// InitLogger is the init routine which returns a new logger instance.
defer l.mutex.Unlock()
l.mdc = make(map[string]string)
}
+
+func (l *MdcLogger) MdcUpdate(key string, value string) {
+ _, ok := l.MdcGet(key)
+ if ok {
+ l.MdcRemove(key)
+ }
+ l.MdcAdd(key, value)
+}
+
+func (l *MdcLogger) ParseFileContent(fileName string) {
+ data, err := ioutil.ReadFile(fileName)
+ if err != nil {
+ fmt.Println("File reading error", err)
+ return
+ }
+ for _, lineData := range strings.Split(string(data), "\n") {
+ if strings.Contains(lineData, "log-level:") {
+ var level = ERR
+ strList := strings.Split(lineData, ":")
+ if strings.Contains(strings.ToUpper(strList[1]), "DEBUG") {
+ level = DEBUG
+ } else if strings.Contains(strings.ToUpper(strList[1]), "INFO") {
+ level = INFO
+ } else if strings.Contains(strings.ToUpper(strList[1]), "ERR") {
+ level = ERR
+ } else if strings.Contains(strings.ToUpper(strList[1]), "WARN") {
+ level = WARN
+ }
+ l.LevelSet(level)
+ }
+ }
+}
+
+func (l *MdcLogger) watch_changes(watcher *inotify.Watcher, fileName string) {
+ for {
+ select {
+ case ev := <-watcher.Event:
+ if strings.Contains(ev.Name, filepath.Dir(fileName)) {
+ l.ParseFileContent(fileName)
+ }
+ case err := <-watcher.Error:
+ fmt.Println("error:", err)
+ }
+ }
+}
+
+func (l *MdcLogger) readEnvVar(envKey string) string {
+ envValue, provided := os.LookupEnv(envKey)
+ if !provided {
+ envValue = ""
+ }
+ return envValue
+}
+
+func (l *MdcLogger) Mdclog_format_initialize(logFileMonitor int) int {
+ ret := -1
+ logFields := []string{"SYSTEM_NAME", "HOST_NAME", "SERVICE_NAME", "CONTAINER_NAME", "POD_NAME"}
+ for _, envKey := range logFields {
+ envValue := l.readEnvVar(envKey)
+ l.MdcUpdate(envKey, envValue)
+ }
+ l.MdcUpdate("PID", strconv.Itoa(os.Getpid()))
+ if logFileMonitor > 0 {
+ watchPath := l.readEnvVar("CONFIG_MAP_NAME")
+ _, err := os.Stat(watchPath)
+ if !os.IsNotExist(err) {
+ if l.init_done == 0 {
+ l.mutex.Lock()
+ l.init_done = 1
+ l.mutex.Unlock()
+ watcher, err := inotify.NewWatcher()
+ if err != nil {
+ return -1
+ }
+ err = watcher.AddWatch(filepath.Dir(watchPath), syscall.IN_CLOSE_WRITE|syscall.IN_CREATE|syscall.IN_CLOSE)
+ if err != nil {
+ return -1
+ }
+ l.ParseFileContent(watchPath)
+ go l.watch_changes(watcher, watchPath)
+ ret = 0
+ }
+ }
+ }
+ return ret
+}
import (
"bytes"
"encoding/json"
+ "io/ioutil"
+ "os"
"testing"
"github.com/stretchr/testify/assert"
logger.Debug("fooo")
assert.Empty(t, logbuffer.String())
}
+
+func TestLogFormatWithMdcArray(t *testing.T) {
+ logger, _ := InitLogger("app")
+ logFileMonitor := 0
+ logger.Mdclog_format_initialize(logFileMonitor)
+ logstr, err := logger.formatLog(INFO, "test")
+ assert.Nil(t, err, "formatLog fails")
+ v := make(map[string]interface{})
+ err = json.Unmarshal(logstr, &v)
+ assert.Equal(t, "INFO", v["crit"])
+ assert.Equal(t, "test", v["msg"])
+ assert.Equal(t, "app", v["id"])
+ _, ok := logger.MdcGet("SYSTEM_NAME")
+ assert.True(t, ok)
+ _, ok = logger.MdcGet("HOST_NAME")
+ assert.True(t, ok)
+ _, ok = logger.MdcGet("SERVICE_NAME")
+ assert.True(t, ok)
+ _, ok = logger.MdcGet("CONTAINER_NAME")
+ assert.True(t, ok)
+ _, ok = logger.MdcGet("POD_NAME")
+ assert.True(t, ok)
+}
+
+func TestLogLevelConfigFileParse(t *testing.T) {
+ logger, _ := InitLogger("app")
+ d1 := []byte("log-level:WARN\n\n")
+ err := ioutil.WriteFile("/tmp/log-file", d1, 0644)
+ assert.Nil(t, err, "Failed to create tmp log-file")
+ os.Setenv("CONFIG_MAP_NAME", "/tmp/log-file")
+ logFileMonitor := 1
+ logger.Mdclog_format_initialize(logFileMonitor)
+ assert.Equal(t, WARN, logger.LevelGet())
+ _, ok := logger.MdcGet("PID")
+ assert.True(t, ok)
+ logger.Mdclog_format_initialize(logFileMonitor)
+}