Skip to content

Commit

Permalink
Feat: simplified logger API
Browse files Browse the repository at this point in the history
  • Loading branch information
hmoog committed Dec 14, 2023
1 parent f2b90f6 commit e78c840
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 20 deletions.
18 changes: 11 additions & 7 deletions log/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,21 +97,25 @@ type Logger interface {
// LogAttrs emits a log message with the given level and attributes.
LogAttrs(msg string, level Level, args ...slog.Attr)

// NewChildLogger creates a new child logger with the given name.
NewChildLogger(name string) (childLogger Logger, shutdown func())
// NewChildLogger creates a new child logger with the given name. If enumerateChildren is true, the child logger
// will extend the name with the number of existing child loggers with the same name.
NewChildLogger(name string, enumerateChildren ...bool) Logger

// NewEntityLogger creates a new logger for an entity with the given name. The logger is automatically shut down
// when the given shutdown event is triggered. The initLogging function is called with the new logger instance and
// can be used to configure the logger.
NewEntityLogger(entityName string) (entityLogger Logger, shutdown func())
// ParentLogger returns the parent of this Logger (or nil if it is the root).
ParentLogger() Logger

// UnsubscribeFromParentLogger unsubscribes the logger from its parent logger (e.g. updates about the log level).
// It is important to call this method whenever we remove all references to the logger, otherwise the logger will
// not be garbage collected.
UnsubscribeFromParentLogger()
}

// NewLogger creates a new logger with the given options.
// If no options are provided, the logger uses the info level and writes to stdout with rfc3339 time format.
func NewLogger(opts ...options.Option[Options]) Logger {
loggerOptions := newOptions(opts...)

l := newLogger(slog.New(loggerOptions.Handler), "", loggerOptions.Name)
l := newLogger(slog.New(loggerOptions.Handler), nil, loggerOptions.Name)
l.SetLogLevel(loggerOptions.Level)

return l
Expand Down
49 changes: 36 additions & 13 deletions log/logger_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"sync/atomic"

"github.com/iotaledger/hive.go/ds/reactive"
"github.com/iotaledger/hive.go/lo"
)

// logger is the default implementation of the Logger interface.
Expand All @@ -26,6 +25,12 @@ type logger struct {
// rootLogger is the root logger instance.
rootLogger *slog.Logger

// parentLogger is the parent logger instance.
parentLogger *logger

// unsubscribeFromParent is the function that is used to unsubscribe from the parent logger.
unsubscribeFromParent func()

// level is the current log level of the logger.
level *slog.LevelVar

Expand All @@ -37,15 +42,24 @@ type logger struct {
}

// newLogger creates a new logger instance with the given name and parent logger.
func newLogger(rootLogger *slog.Logger, parentPath, name string) *logger {
func newLogger(rootLogger *slog.Logger, parentLogger *logger, name string) *logger {
l := &logger{
name: name,
path: lo.Cond(parentPath == "", name, parentPath+"."+name),
path: name,
rootLogger: rootLogger,
parentLogger: parentLogger,
level: new(slog.LevelVar),
reactiveLevel: reactive.NewVariable[Level](),
}

if parentLogger != nil {
if parentLogger.path != "" {
l.path = parentLogger.path + "." + l.path
}

l.unsubscribeFromParent = l.reactiveLevel.InheritFrom(parentLogger.reactiveLevel)
}

l.reactiveLevel.OnUpdate(func(_, newLevel Level) { l.level.Set(newLevel) })

return l
Expand Down Expand Up @@ -251,24 +265,33 @@ func (l *logger) LogAttrs(msg string, level Level, args ...slog.Attr) {
}

// NewChildLogger creates a new child logger with the given name.
func (l *logger) NewChildLogger(name string) (childLogger Logger, shutdown func()) {
func (l *logger) NewChildLogger(name string, enumerateChildren ...bool) (childLogger Logger) {
if l == nil {
return l, func() {}
return l
}

nestedLoggerInstance := newLogger(l.rootLogger, l.path, name)
if len(enumerateChildren) > 0 && enumerateChildren[0] {
name = l.uniqueEntityName(name)
}

return nestedLoggerInstance, nestedLoggerInstance.reactiveLevel.InheritFrom(l.reactiveLevel)
return newLogger(l.rootLogger, l, name)
}

// NewEntityLogger is identical to NewChildLogger with the difference that the name of the logger is automatically
// extended with a unique identifier to avoid name collisions.
func (l *logger) NewEntityLogger(entityName string) (entityLogger Logger, shutdown func()) {
if l == nil {
return l, func() {}
// ParentLogger returns the parent logger of the logger (or nil if it is the root).
func (l *logger) ParentLogger() Logger {
if l.parentLogger == nil {
return nil
}

return l.NewChildLogger(l.uniqueEntityName(entityName))
return l.parentLogger
}

// UnsubscribeFromParentLogger unsubscribes the logger from its parent logger (e.g. updates about the log level).
// It is important to call this method whenever we rem

Check failure on line 290 in log/logger_impl.go

View workflow job for this annotation

GitHub Actions / golangci-log

[golangci-log] log/logger_impl.go#L290

Comment should end in a period (godot)
Raw output
logger_impl.go:290:55: Comment should end in a period (godot)
// It is important to call this method whenever we rem
                                                      ^
func (l *logger) UnsubscribeFromParentLogger() {
if l.unsubscribeFromParent != nil {
l.unsubscribeFromParent()
}
}

// uniqueEntityName returns the name of an embedded instance of the given type.
Expand Down

0 comments on commit e78c840

Please sign in to comment.