diff --git a/log/logger.go b/log/logger.go index 060ded548d286d1a863f6bd33d9d0b9edd809fb4..adcaa5dc590a7b46e9a4d0906afbd242e904a6ca 100644 --- a/log/logger.go +++ b/log/logger.go @@ -3,7 +3,12 @@ package log import ( "fmt" "io" + "log/syslog" "os" + "reflect" + "runtime" + "strconv" + "strings" "sync" "time" ) @@ -14,22 +19,47 @@ 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 - Loglevel Level - lock sync.Mutex + 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, - Loglevel: INFO, - lock: sync.Mutex{}, + DefaultWriter: os.Stderr, + LoglevelWriter: make(map[Level]io.Writer), + toSyslog: make(map[Level]bool), + Loglevel: INFO, + lock: sync.Mutex{}, } }) return logger } +//Loglevel sets the verbosity of the logger +//Defaults to INFO func Loglevel(level Level) { l := get() l.lock.Lock() @@ -37,48 +67,101 @@ 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 { case PANIC: diff --git a/main.go b/main.go index 7527c7d3e71b2c3548031f90f24772174560a41c..800252d804efd745aac2b87bd45ee5ce3df6ec66 100644 --- a/main.go +++ b/main.go @@ -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) diff --git a/nucleus/nucleus-core.go b/nucleus/nucleus-core.go index 20a504de5725d56ccc752bf5816c9c962bfed52d..1a2dfef3853df2a7acb12ecb85e39515e24a0ff2 100644 --- a/nucleus/nucleus-core.go +++ b/nucleus/nucleus-core.go @@ -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")