Skip to content
Snippets Groups Projects
Commit aad7e9a3 authored by Manuel Kieweg's avatar Manuel Kieweg
Browse files

Merge branch '25-enhanced-logging' into 'develop'

Resolve "Enhanced Logging"

See merge request cocsn/gosdn!41
parents ccf264ba 06b89cc4
No related branches found
No related tags found
2 merge requests!41Resolve "Enhanced Logging",!18Develop
Pipeline #52507 passed
......@@ -3,7 +3,12 @@ package log
import (
"fmt"
"io"
"log/syslog"
"os"
"reflect"
"runtime"
"strconv"
"strings"
"sync"
"time"
)
......@@ -14,15 +19,38 @@ var once sync.Once
// Logger is a wrapper for log.Logger and provides
// methods to enable and disable logging.
type Logger struct {
Out io.Writer
DefaultWriter io.Writer
LoglevelWriter map[Level]io.Writer
toSyslog map[Level]bool
Loglevel Level
lock sync.Mutex
builder strings.Builder
}
func (l *Logger) buildMessage(level Level, syslog bool, args ...interface{}) {
if !syslog {
l.builder.WriteString(time.Now().Format(time.RFC3339))
}
l.builder.WriteRune('\t')
l.builder.WriteString(prefix(level))
l.builder.WriteRune('\t')
function, line := callers()
functionSplitted := strings.SplitAfter(function, "/")
function = functionSplitted[len(functionSplitted)-1]
l.builder.WriteString(function)
l.builder.WriteRune(':')
l.builder.WriteString(strconv.Itoa(line))
l.builder.WriteRune('\t')
l.builder.WriteString(fmt.Sprint(args...))
l.builder.WriteRune('\n')
}
func get() *Logger {
once.Do(func() {
logger = &Logger{
Out: os.Stdout,
DefaultWriter: os.Stderr,
LoglevelWriter: make(map[Level]io.Writer),
toSyslog: make(map[Level]bool),
Loglevel: INFO,
lock: sync.Mutex{},
}
......@@ -30,6 +58,8 @@ func get() *Logger {
return logger
}
//Loglevel sets the verbosity of the logger
//Defaults to INFO
func Loglevel(level Level) {
l := get()
l.lock.Lock()
......@@ -37,47 +67,100 @@ func Loglevel(level Level) {
l.Loglevel = level
}
//Output defines the output of the logger
//Defaults to os.Stderr
func Output(out io.Writer) {
l := get()
l.lock.Lock()
defer l.lock.Unlock()
l.Out = out
l.DefaultWriter = out
}
//LoglevelOutput defines a special output
//for a certain log level
func LoglevelOutput(level Level, out io.Writer) {
l := get()
l.lock.Lock()
defer l.lock.Unlock()
l.LoglevelWriter[level] = out
if reflect.TypeOf(out) == reflect.TypeOf(&syslog.Writer{}) {
l.toSyslog[level] = true
}
}
//Debug passes the DEBUG flag and a
//message to the logger
func Debug(args ...interface{}) {
log(DEBUG, args...)
}
//Debug passes the DEBUG flag and a
//message to the logger
func Info(args ...interface{}) {
log(INFO, args...)
}
//Warn passes the WARNING flag and a
//message to the logger
func Warn(args ...interface{}) {
log(WARNING, args...)
}
//Error passes the ERROR flag and a
//message to the logger
func Error(args ...interface{}) {
log(ERROR, args...)
}
//Fatal passes the FATAL flag and a
//message to the logger and calls
//os.Exit(1)
func Fatal(args ...interface{}) {
log(FATAL, args...)
os.Exit(1)
}
//Panic passes the PANIC flag and a
//message to the logger
//Also calls builtin.panic()
func Panic(args ...interface{}) {
log(PANIC, args...)
panic(args)
}
func log(level Level, args ...interface{}) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
l := get()
l.lock.Lock()
defer l.lock.Unlock()
defer l.builder.Reset()
if level <= l.Loglevel {
msg := fmt.Sprint(args...)
logMessage := time.Now().Format(time.RFC3339) + "\t" + prefix(level) + "\t" + msg + "\n"
l.Out.Write([]byte(logMessage))
l.buildMessage(level, l.toSyslog[level], args...)
msg := []byte(l.builder.String())
writer, ok := l.LoglevelWriter[level]
var err error
if !ok {
_, err = l.DefaultWriter.Write(msg)
} else {
_, err = writer.Write(msg)
}
if err != nil {
panic(err)
}
}
}
func callers() (string, int) {
pc := make([]uintptr, 15)
n := runtime.Callers(5, pc)
frames := runtime.CallersFrames(pc[:n])
frame, _ := frames.Next()
return frame.Function, frame.Line
}
func prefix(level Level) string {
switch level {
......
......@@ -4,6 +4,7 @@ import (
"code.fbi.h-da.de/cocsn/gosdn/log"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"flag"
"log/syslog"
)
func main() {
......@@ -17,6 +18,16 @@ func main() {
cliSocket := *cliListenAddr + ":" + *cliListenPort
log.Loglevel(log.DEBUG)
syslogWriter, err := syslog.New(syslog.LOG_ALERT, "gosdn")
defer func() {
if err := syslogWriter.Close(); err != nil {
log.Fatal(err)
}
}()
if err != nil {
log.Fatal(err)
}
log.LoglevelOutput(log.INFO, syslogWriter)
// Setup a channel to communicate if goSDN should shutdown.
IsRunningChannel := make(chan bool)
......
......@@ -7,10 +7,7 @@ import (
"time"
)
/*
* This function is used to start the core of the controller and any auxiliary services.
*/
//StartAndRun is used to start the core of the controller and any auxiliary services.
func StartAndRun(socket, filename string, IsRunningChannel chan bool) {
log.Info("This is the network superintendent...")
log.Info("Starting my ducks")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment