summaryrefslogtreecommitdiffstats
path: root/pkg/logging/logging.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pkg/logging/logging.go131
1 files changed, 131 insertions, 0 deletions
diff --git a/pkg/logging/logging.go b/pkg/logging/logging.go
new file mode 100644
index 0000000..e310695
--- /dev/null
+++ b/pkg/logging/logging.go
@@ -0,0 +1,131 @@
+package logging
+
+import (
+ "fmt"
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+ "os"
+ "sync"
+ "time"
+)
+
+const (
+ CONSOLE = "console"
+ JOURNAL = "systemd-journald"
+)
+
+// defaultEncConfig defines the default zapcore.EncoderConfig for the logging package.
+var defaultEncConfig = zapcore.EncoderConfig{
+ TimeKey: "ts",
+ LevelKey: "level",
+ NameKey: "logger",
+ CallerKey: "caller",
+ MessageKey: "msg",
+ StacktraceKey: "stacktrace",
+ LineEnding: zapcore.DefaultLineEnding,
+ EncodeLevel: zapcore.CapitalLevelEncoder,
+ EncodeTime: zapcore.ISO8601TimeEncoder,
+ EncodeDuration: zapcore.StringDurationEncoder,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+}
+
+// Options define child loggers with their desired log level.
+type Options map[string]zapcore.Level
+
+// Logging implements access to a default logger and named child loggers.
+// Log levels can be configured per named child via Options which, if not configured,
+// fall back on a default log level.
+// Logs either to the console or to systemd-journald.
+type Logging struct {
+ logger *Logger
+ output string
+ verbosity zap.AtomicLevel
+ interval time.Duration
+
+ // coreFactory creates zapcore.Core based on the log level and the log output.
+ coreFactory func(zap.AtomicLevel) zapcore.Core
+
+ mu sync.Mutex
+ loggers map[string]*Logger
+
+ options Options
+}
+
+// NewLogging takes the name and log level for the default logger,
+// output where log messages are written to,
+// options having log levels for named child loggers
+// and returns a new Logging.
+func NewLogging(name string, level zapcore.Level, output string, options Options, interval time.Duration) (*Logging, error) {
+ verbosity := zap.NewAtomicLevelAt(level)
+
+ var coreFactory func(zap.AtomicLevel) zapcore.Core
+ switch output {
+ case CONSOLE:
+ enc := zapcore.NewConsoleEncoder(defaultEncConfig)
+ ws := zapcore.Lock(os.Stderr)
+ coreFactory = func(verbosity zap.AtomicLevel) zapcore.Core {
+ return zapcore.NewCore(enc, ws, verbosity)
+ }
+ case JOURNAL:
+ coreFactory = func(verbosity zap.AtomicLevel) zapcore.Core {
+ return NewJournaldCore(name, verbosity)
+ }
+ default:
+ return nil, invalidOutput(output)
+ }
+
+ logger := NewLogger(zap.New(coreFactory(verbosity)).Named(name).Sugar(), interval)
+
+ return &Logging{
+ logger: logger,
+ output: output,
+ verbosity: verbosity,
+ interval: interval,
+ coreFactory: coreFactory,
+ loggers: make(map[string]*Logger),
+ options: options,
+ },
+ nil
+}
+
+// GetChildLogger returns a named child logger.
+// Log levels for named child loggers are obtained from the logging options and, if not found,
+// set to the default log level.
+func (l *Logging) GetChildLogger(name string) *Logger {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+
+ if logger, ok := l.loggers[name]; ok {
+ return logger
+ }
+
+ var verbosity zap.AtomicLevel
+ if level, found := l.options[name]; found {
+ verbosity = zap.NewAtomicLevelAt(level)
+ } else {
+ verbosity = l.verbosity
+ }
+
+ logger := NewLogger(zap.New(l.coreFactory(verbosity)).Named(name).Sugar(), l.interval)
+ l.loggers[name] = logger
+
+ return logger
+}
+
+// GetLogger returns the default logger.
+func (l *Logging) GetLogger() *Logger {
+ return l.logger
+}
+
+// AssertOutput returns an error if output is not a valid logger output.
+func AssertOutput(o string) error {
+ if o == CONSOLE || o == JOURNAL {
+ return nil
+ }
+
+ return invalidOutput(o)
+}
+
+func invalidOutput(o string) error {
+ return fmt.Errorf("%s is not a valid logger output. Must be either %q or %q", o, CONSOLE, JOURNAL)
+}