treewide: introduce osbase package and move things around
All except localregistry moved from metropolis/pkg to osbase,
localregistry moved to metropolis/test as its only used there anyway.
Change-Id: If1a4bf377364bef0ac23169e1b90379c71b06d72
Reviewed-on: https://review.monogon.dev/c/monogon/+/3079
Tested-by: Jenkins CI
Reviewed-by: Serge Bazanski <serge@monogon.tech>
diff --git a/osbase/logtree/logtree_publisher.go b/osbase/logtree/logtree_publisher.go
new file mode 100644
index 0000000..6c4120a
--- /dev/null
+++ b/osbase/logtree/logtree_publisher.go
@@ -0,0 +1,229 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package logtree
+
+import (
+ "fmt"
+ "io"
+ "runtime"
+ "strings"
+ "time"
+
+ "source.monogon.dev/osbase/logbuffer"
+)
+
+type leveledPublisher struct {
+ node *node
+ depth int
+}
+
+// LeveledFor returns a LeveledLogger publishing interface for a given DN. An error
+// may be returned if the DN is malformed.
+func (l *LogTree) LeveledFor(dn DN) (LeveledLogger, error) {
+ node, err := l.nodeByDN(dn)
+ if err != nil {
+ return nil, err
+ }
+ return &leveledPublisher{
+ node: node,
+ depth: 0,
+ }, nil
+}
+
+func (l *LogTree) RawFor(dn DN) (io.Writer, error) {
+ node, err := l.nodeByDN(dn)
+ if err != nil {
+ return nil, fmt.Errorf("could not retrieve raw logger: %w", err)
+ }
+ return node.rawLineBuffer, nil
+}
+
+// MustLeveledFor returns a LeveledLogger publishing interface for a given DN, or
+// panics if the given DN is invalid.
+func (l *LogTree) MustLeveledFor(dn DN) LeveledLogger {
+ leveled, err := l.LeveledFor(dn)
+ if err != nil {
+ panic(fmt.Errorf("LeveledFor returned: %w", err))
+ }
+ return leveled
+}
+
+func (l *LogTree) MustRawFor(dn DN) io.Writer {
+ raw, err := l.RawFor(dn)
+ if err != nil {
+ panic(fmt.Errorf("RawFor returned: %w", err))
+ }
+ return raw
+}
+
+// SetVerbosity sets the verbosity for a given DN (non-recursively, ie. for that DN
+// only, not its children).
+func (l *LogTree) SetVerbosity(dn DN, level VerbosityLevel) error {
+ node, err := l.nodeByDN(dn)
+ if err != nil {
+ return err
+ }
+ node.verbosity = level
+ return nil
+}
+
+// logRaw is called by this node's LineBuffer any time a raw log line is completed.
+// It will create a new entry, append it to the journal, and notify all pertinent
+// subscribers.
+func (n *node) logRaw(line *logbuffer.Line) {
+ e := &entry{
+ origin: n.dn,
+ raw: line,
+ }
+ n.tree.journal.append(e)
+ n.tree.journal.notify(e)
+}
+
+// LogExternalLeveled injects a ExternalLeveledPayload into a given
+// LeveledLogger. This should only be used by systems which translate external
+// data sources into leveled logging - see ExternelLeveledPayload for more
+// information.
+func LogExternalLeveled(l LeveledLogger, e *ExternalLeveledPayload) error {
+ publisher, ok := l.(*leveledPublisher)
+ if !ok {
+ return fmt.Errorf("the given LeveledLogger is not a *leveledPublisher")
+ }
+ p := e.sanitize()
+ entry := &entry{
+ origin: publisher.node.dn,
+ leveled: p,
+ }
+ publisher.node.tree.journal.append(entry)
+ publisher.node.tree.journal.notify(entry)
+ return nil
+}
+
+// log builds a LeveledPayload and entry for a given message, including all related
+// metadata. It will create a new entry append it to the journal, and notify all
+// pertinent subscribers.
+func (l *leveledPublisher) logLeveled(depth int, severity Severity, msg string) {
+ _, file, line, ok := runtime.Caller(2 + depth)
+ if !ok {
+ file = "???"
+ line = 1
+ } else {
+ slash := strings.LastIndex(file, "/")
+ if slash >= 0 {
+ file = file[slash+1:]
+ }
+ }
+
+ // Remove leading/trailing newlines and split.
+ messages := strings.Split(strings.Trim(msg, "\n"), "\n")
+
+ p := &LeveledPayload{
+ timestamp: time.Now(),
+ severity: severity,
+ messages: messages,
+ file: file,
+ line: line,
+ }
+ e := &entry{
+ origin: l.node.dn,
+ leveled: p,
+ }
+ l.node.tree.journal.append(e)
+ l.node.tree.journal.notify(e)
+}
+
+// Info implements the LeveledLogger interface.
+func (l *leveledPublisher) Info(args ...interface{}) {
+ l.logLeveled(l.depth, INFO, fmt.Sprint(args...))
+}
+
+// Infof implements the LeveledLogger interface.
+func (l *leveledPublisher) Infof(format string, args ...interface{}) {
+ l.logLeveled(l.depth, INFO, fmt.Sprintf(format, args...))
+}
+
+// Warning implements the LeveledLogger interface.
+func (l *leveledPublisher) Warning(args ...interface{}) {
+ l.logLeveled(l.depth, WARNING, fmt.Sprint(args...))
+}
+
+// Warningf implements the LeveledLogger interface.
+func (l *leveledPublisher) Warningf(format string, args ...interface{}) {
+ l.logLeveled(l.depth, WARNING, fmt.Sprintf(format, args...))
+}
+
+// Error implements the LeveledLogger interface.
+func (l *leveledPublisher) Error(args ...interface{}) {
+ l.logLeveled(l.depth, ERROR, fmt.Sprint(args...))
+}
+
+// Errorf implements the LeveledLogger interface.
+func (l *leveledPublisher) Errorf(format string, args ...interface{}) {
+ l.logLeveled(l.depth, ERROR, fmt.Sprintf(format, args...))
+}
+
+// Fatal implements the LeveledLogger interface.
+func (l *leveledPublisher) Fatal(args ...interface{}) {
+ l.logLeveled(l.depth, FATAL, fmt.Sprint(args...))
+}
+
+// Fatalf implements the LeveledLogger interface.
+func (l *leveledPublisher) Fatalf(format string, args ...interface{}) {
+ l.logLeveled(l.depth, FATAL, fmt.Sprintf(format, args...))
+}
+
+// WithAddedStackDepth impleemnts the LeveledLogger interface.
+func (l *leveledPublisher) WithAddedStackDepth(depth int) LeveledLogger {
+ l2 := *l
+ l2.depth += depth
+ return &l2
+}
+
+// V implements the LeveledLogger interface.
+func (l *leveledPublisher) V(v VerbosityLevel) VerboseLeveledLogger {
+ return &verbose{
+ publisher: l,
+ enabled: l.node.verbosity >= v,
+ }
+}
+
+// verbose implements the VerboseLeveledLogger interface. It is a thin wrapper
+// around node, with an 'enabled' bool. This means that V(n)-returned
+// VerboseLeveledLoggers must be short lived, as a changed in verbosity will not
+// affect all already existing VerboseLeveledLoggers.
+type verbose struct {
+ publisher *leveledPublisher
+ node *node
+ enabled bool
+}
+
+func (v *verbose) Enabled() bool {
+ return v.enabled
+}
+
+func (v *verbose) Info(args ...interface{}) {
+ if !v.enabled {
+ return
+ }
+ v.publisher.logLeveled(v.publisher.depth, INFO, fmt.Sprint(args...))
+}
+
+func (v *verbose) Infof(format string, args ...interface{}) {
+ if !v.enabled {
+ return
+ }
+ v.publisher.logLeveled(v.publisher.depth, INFO, fmt.Sprintf(format, args...))
+}