bus/logger/log_provider.go

Summary

Maintainability
A
0 mins
Test Coverage
B
81%
package logger

import (
    "fmt"
    "os"
    "runtime"
    "sync"
    "time"

    "github.com/lugu/qiloop/bus"
    "github.com/lugu/qiloop/bus/util"
)

// ErrNotImplemented is returned when a method is not supported by
// this implementation.
var ErrNotImplemented = fmt.Errorf("Not supported")

type logProvider struct {
    manager        LogManagerProxy
    id             int32
    location       string
    category       string
    logger         Logger
    verbosity      LogLevel
    verbosityMutex sync.RWMutex
    filters        map[string]LogLevel
    filtersMutex   sync.RWMutex
}

// Logger sends a log message to the log manager if a log listener
// request it, else it does nothing, except Fatal which calls
// os.Exit(1).
type Logger interface {
    Fatal(format string, v ...interface{})
    Error(format string, v ...interface{})
    Warning(format string, v ...interface{})
    Info(format string, v ...interface{})
    Verbose(format string, v ...interface{})
    Debug(format string, v ...interface{})
    Terminate()
}

func newLogMessage(level LogLevel, location, category,
    msg string) LogMessage {
    now := time.Now().UnixNano()

    _, file, line, ok := runtime.Caller(2)
    if !ok {
        file = "???"
        line = 0
    }

    return LogMessage{
        Source:     fmt.Sprintf("%s:%d", file, line),
        Level:      level,
        Category:   category,
        Location:   location,
        Message:    msg,
        Id:         0,
        Date:       TimePoint{uint64(now)},
        SystemDate: TimePoint{uint64(now)},
    }
}

func newLogProviderImpl(category string) (LogProviderImplementor, *logProvider) {
    location := fmt.Sprintf("%s:%d", util.MachineID(), util.ProcessID())
    impl := &logProvider{
        location:  location,
        category:  category,
        verbosity: LogLevelInfo,
        filters:   make(map[string]LogLevel),
    }
    return impl, impl
}

func (l *logProvider) logf(level LogLevel, format string, v ...interface{}) {
    filterLevel, ok := l.filters[l.category]
    if ok && level.Level > filterLevel.Level {
        return
    }
    l.verbosityMutex.RLock()
    if level.Level > l.verbosity.Level {
        l.verbosityMutex.RUnlock()
        return
    }
    l.verbosityMutex.RUnlock()

    msg := fmt.Sprintf(format, v...)
    logMsg := newLogMessage(level, l.location, l.category, msg)
    l.manager.Log([]LogMessage{logMsg})
}

func (l *logProvider) Fatal(format string, v ...interface{}) {
    l.logf(LogLevelFatal, format, v...)
    os.Exit(1)
}

func (l *logProvider) Error(format string, v ...interface{}) {
    l.logf(LogLevelError, format, v...)
}

func (l *logProvider) Warning(format string, v ...interface{}) {
    l.logf(LogLevelWarning, format, v...)
}

func (l *logProvider) Info(format string, v ...interface{}) {
    l.logf(LogLevelInfo, format, v...)
}

func (l *logProvider) Verbose(format string, v ...interface{}) {
    l.logf(LogLevelVerbose, format, v...)
}

func (l *logProvider) Debug(format string, v ...interface{}) {
    l.logf(LogLevelDebug, format, v...)
}

func (l *logProvider) Terminate() {
    l.manager.RemoveProvider(l.id)
}

func (l *logProvider) Activate(activation bus.Activation,
    helper LogProviderSignalHelper) (err error) {
    l.manager, err = LogManager(activation.Session)
    if err != nil {
        return fmt.Errorf("Cannot create LogProvider: %s", err)
    }
    return nil
}
func (l *logProvider) OnTerminate() {
    l.SetVerbosity(LogLevelNone)
}
func (l *logProvider) SetVerbosity(level LogLevel) error {
    l.verbosityMutex.Lock()
    l.verbosity = level
    l.verbosityMutex.Unlock()
    return nil
}
func (l *logProvider) SetCategory(category string, level LogLevel) error {
    if category == l.category {
        l.SetVerbosity(level)
    }
    return nil
}
func (l *logProvider) ClearAndSet(filters map[string]LogLevel) error {
    for filter, level := range filters {
        l.SetCategory(filter, level)
    }
    return nil
}

// NewLogger returns a new Logger. NewLogger creates a new log
// provider and associate it with log manager.
func NewLogger(session bus.Session, category string) (Logger, error) {
    logManager, err := LogManager(session)
    if err != nil {
        return nil, err
    }
    service := logManager.Proxy().ProxyService(session)
    impl, logger := newLogProviderImpl(category)
    proxy, err := CreateLogProvider(session, service, impl)
    logger.id, err = logManager.AddProvider(proxy)
    if err != nil {
        return nil, err
    }
    return logger, err
}