Skip to content

Commit

Permalink
1. Moved some common level name methods/types from native/level to …
Browse files Browse the repository at this point in the history
…`level`.

2. Introduced a `LoggerFacade` interface with some more utility methods.
  • Loading branch information
blaubaer committed Jan 25, 2023
1 parent c4dafbf commit 1adf114
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 112 deletions.
20 changes: 13 additions & 7 deletions level/level.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// important is the event. Trace is the less severe and Fatal the
// most severe.
//
// Customization
// # Customization
//
// Different implementations of Provider could introduce more Levels. All
// ordinals are unique over all instances of Level. Info = 3000 will be
Expand All @@ -11,6 +11,8 @@
// instances of Level. Standard levels always remains available.
package level

import "errors"

const (
// Trace defines the lowest possible level. This is usually only used
// in cases where really detailed logs are required. For example to document
Expand All @@ -19,7 +21,7 @@ const (
Trace Level = 1000

// Debug is used to document information which is used to debug
// problems. These information are in regular operation mode are not
// problems. This information are in regular operation mode are not
// required; but could help once enabled to track down common issues.
Debug Level = 2000

Expand All @@ -30,21 +32,21 @@ const (
Info Level = 3000

// Warn implies that something happened which failed but could be
// recovered gracefully. In best case the user does not noticed anything.
// recovered gracefully. In best case the user does not notice anything.
// But personal should investigate as soon as possible to prevent something
// like this happening again.
Warn Level = 4000

// Error implies that something happened which failed and cannot be
// recovered gracefully but it only affects one or a small amount of users
// recovered gracefully, but it only affects one or a small amount of users
// and the rest of the system can continue to work. Personal should
// investigate right now to prevent such stuff happening again and recover
// broken users.
Error Level = 5000

// Fatal implies that something happened which failed and cannot be
// recovered gracefully, which might affect every user of the system. This
// implies that the whole system is not longer operable and should/will be
// implies that the whole system is no longer operable and should/will be
// shutdown (if possible gracefully) right now. Personal is required to
// investigate right now to prevent such stuff happening again, bring the
// the system back to operations and recover broken users.
Expand All @@ -60,9 +62,9 @@ func (instance Level) CompareTo(o Level) int {
return int(instance) - int(o)
}

// Levels represents a slice of 0..n Level.
// Levels represents a slice of 0...n Level.
//
// Additionally it implements the sort.Interface to enable to call sort.Sort
// Additionally, it implements the sort.Interface to enable to call sort.Sort
// to order the contents of this slice by its severity.
type Levels []Level

Expand Down Expand Up @@ -99,3 +101,7 @@ func (instance levelsAsProvider) GetName() string {
func (instance levelsAsProvider) GetLevels() Levels {
return instance.values
}

// ErrIllegalLevel represents that an illegal level.Level value/name was
// provided.
var ErrIllegalLevel = errors.New("illegal level")
41 changes: 41 additions & 0 deletions level/names.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package level

// Names is used to make readable names out of level.Level or the other way
// around.
type Names interface {
// ToName converts a given level.Level to a human-readable name. If this
// level is unknown by this instance an error is returned. Most likely
// ErrIllegalLevel.
ToName(Level) (string, error)

// ToLevel converts a given human-readable name to a level.Level. If this
// name is unknown by this instance an error is returned. Most likely
// ErrIllegalLevel.
ToLevel(string) (Level, error)
}

// NamesAware represents an object that is aware of Names.
type NamesAware interface {
// GetLevelNames returns an instance of level.Names that support by
// formatting levels in a human-readable format.
GetLevelNames() Names
}

// NewNamesFacade creates a facade of Names using the given provider.
func NewNamesFacade(provider func() Names) Names {
return namesFacade(provider)
}

type namesFacade func() Names

func (instance namesFacade) ToName(lvl Level) (string, error) {
return instance.Unwrap().ToName(lvl)
}

func (instance namesFacade) ToLevel(name string) (Level, error) {
return instance.Unwrap().ToLevel(name)
}

func (instance namesFacade) Unwrap() Names {
return instance()
}
67 changes: 67 additions & 0 deletions level/names_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package level

import (
"errors"
"testing"

"github.com/echocat/slf4g/internal/test/assert"
)

func Test_NewNamesFacade(t *testing.T) {
givenNames := &mockNames{}

actual := NewNamesFacade(func() Names {
return givenNames
})

assert.ToBeSame(t, givenNames, actual.(namesFacade)())
}

func Test_namesFacade_ToName(t *testing.T) {
givenLevel := Warn
givenError := errors.New("foo")
givenNames := &mockNames{onToName: func(actualLevel Level) (string, error) {
assert.ToBeEqual(t, givenLevel, actualLevel)
return "bar", givenError
}}
instance := namesFacade(func() Names { return givenNames })

actual, actualErr := instance.ToName(givenLevel)

assert.ToBeEqual(t, "bar", actual)
assert.ToBeSame(t, givenError, actualErr)
}

func Test_namesFacade_ToLevel(t *testing.T) {
givenLevel := Warn
givenError := errors.New("foo")
givenNames := &mockNames{onToLevel: func(actualName string) (Level, error) {
assert.ToBeEqual(t, "bar", actualName)
return givenLevel, givenError
}}
instance := namesFacade(func() Names { return givenNames })

actual, actualErr := instance.ToLevel("bar")

assert.ToBeEqual(t, givenLevel, actual)
assert.ToBeSame(t, givenError, actualErr)
}

type mockNames struct {
onToName func(lvl Level) (string, error)
onToLevel func(name string) (Level, error)
}

func (instance *mockNames) ToName(l Level) (string, error) {
if v := instance.onToName; v != nil {
return v(l)
}
panic("not implemented")
}

func (instance *mockNames) ToLevel(s string) (Level, error) {
if v := instance.onToLevel; v != nil {
return v(s)
}
panic("not implemented")
}
23 changes: 19 additions & 4 deletions logger.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package log

import "github.com/echocat/slf4g/fields"
import (
"github.com/echocat/slf4g/fields"
"github.com/echocat/slf4g/level"
)

// Logger defines an instance which executes log event actions.
//
// Implementation hints
// # Implementation hints
//
// If you considering to implement slf4g you're usually not required to
// If you're considering to implement slf4g you're usually not required to
// implement a full instance of Logger. Usually you just need to implement
// CoreLogger and call NewLogger(with the CoreLogger) to create a full
// implemented instance of a Logger.
Expand Down Expand Up @@ -123,11 +126,23 @@ func NewLogger(cl CoreLogger) Logger {
return NewLoggerFacade(func() CoreLogger { return cl })
}

// LoggerFacade is a fully implemented logger with utility methods for easier
// implementation of delegates.
type LoggerFacade interface {
Logger

// DoLog is acting as a simple log for the given level.
DoLog(level level.Level, skipFrames uint16, args ...interface{})

// DoLogf is acting as a formatted log for the given level.
DoLogf(level level.Level, skipFrames uint16, format string, args ...interface{})
}

// NewLoggerFacade is like NewLogger but takes a provider function that can
// potentially return every time another instance of a CoreLogger. This is
// useful especially in cases where you want to deal with concurrency while
// creation of objects that need to hold a reference to a Logger.
func NewLoggerFacade(provider func() CoreLogger) Logger {
func NewLoggerFacade(provider func() CoreLogger) LoggerFacade {
return &loggerImpl{
coreProvider: provider,
fields: fields.Empty(),
Expand Down
14 changes: 11 additions & 3 deletions logger_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ func (instance *loggerImpl) GetProvider() Provider {
}

func (instance *loggerImpl) log(level level.Level, args ...interface{}) {
instance.DoLog(level, 2, args...)
}

func (instance *loggerImpl) logf(level level.Level, format string, args ...interface{}) {
instance.DoLogf(level, 2, format, args...)
}

func (instance *loggerImpl) DoLog(level level.Level, skipFrames uint16, args ...interface{}) {
if !instance.IsLevelEnabled(level) {
return
}
Expand All @@ -71,18 +79,18 @@ func (instance *loggerImpl) log(level level.Level, args ...interface{}) {
e = e.With(provider.GetFieldKeysSpec().GetMessage(), args)
}

instance.Unwrap().Log(e, 2)
instance.Unwrap().Log(e, skipFrames+1)
}

func (instance *loggerImpl) logf(level level.Level, format string, args ...interface{}) {
func (instance *loggerImpl) DoLogf(level level.Level, skipFrames uint16, format string, args ...interface{}) {
if !instance.IsLevelEnabled(level) {
return
}
provider := instance.GetProvider()
e := instance.NewEventWithFields(level, instance.fields).
Withf(provider.GetFieldKeysSpec().GetMessage(), format, args...)

instance.Unwrap().Log(e, 2)
instance.Unwrap().Log(e, skipFrames+1)
}

func (instance *loggerImpl) Trace(args ...interface{}) {
Expand Down
4 changes: 2 additions & 2 deletions native/facade/value/level.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@ func (instance Level) UnmarshalText(text []byte) error {
return instance.Set(string(text))
}

func (instance Level) getNames() nlevel.Names {
func (instance Level) getNames() level.Names {
if v := instance.Names; v != nil {
return v
}
if va, ok := instance.Target.(nlevel.NamesAware); ok {
if va, ok := instance.Target.(level.NamesAware); ok {
if v := va.GetLevelNames(); v != nil {
return v
}
Expand Down
4 changes: 2 additions & 2 deletions native/formatter/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package formatter

import (
"fmt"
"github.com/echocat/slf4g/level"

"github.com/echocat/slf4g/native/execution"
"github.com/echocat/slf4g/native/formatter/encoding"

log "github.com/echocat/slf4g"
"github.com/echocat/slf4g/fields"
"github.com/echocat/slf4g/native/hints"
nlevel "github.com/echocat/slf4g/native/level"
)

const (
Expand Down Expand Up @@ -97,7 +97,7 @@ func (instance *Json) getLevelFormatter(using log.Provider) Level {
if v := instance.LevelFormatter; v != nil {
return v
}
if v, ok := using.(nlevel.NamesAware); ok {
if v, ok := using.(level.NamesAware); ok {
return NewNamesBasedLevel(v.GetLevelNames())
}
return DefaultLevel
Expand Down
4 changes: 2 additions & 2 deletions native/formatter/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,10 @@ func Test_Json_encodeValuesChecked(t *testing.T) {
type someProvider struct {
log.Provider

names nlevel.Names
names level.Names
}

func (instance *someProvider) GetLevelNames() nlevel.Names {
func (instance *someProvider) GetLevelNames() level.Names {
return instance
}

Expand Down
2 changes: 1 addition & 1 deletion native/formatter/level.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

// DefaultLevel is the default instance of Level which should cover the most of
// the cases.
var DefaultLevel Level = NewNamesBasedLevel(nlevel.NewNamesFacade(func() nlevel.Names {
var DefaultLevel Level = NewNamesBasedLevel(level.NewNamesFacade(func() level.Names {
return nlevel.DefaultNames
}))

Expand Down
2 changes: 1 addition & 1 deletion native/formatter/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (instance TemplateRenderingContext) Hints() hints.Hints {
// LevelNames is a convenience method to easy return the current nlevel.Names
// of the corresponding log.Provider.
func (instance TemplateRenderingContext) LevelNames() nlevel.Names {
if v, ok := instance.Provider.(nlevel.NamesAware); ok {
if v, ok := instance.Provider.(level.NamesAware); ok {
if names := v.GetLevelNames(); names != nil {
return names
}
Expand Down
2 changes: 1 addition & 1 deletion native/formatter/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ func (instance *Text) formatLevelChecked(l level.Level, using log.Provider, to *
}

func (instance *Text) getLevelNames(using log.Provider) nlevel.Names {
if v, ok := using.(nlevel.NamesAware); ok {
if v, ok := using.(level.NamesAware); ok {
return v.GetLevelNames()
}
if v := nlevel.DefaultNames; v != nil {
Expand Down
Loading

0 comments on commit 1adf114

Please sign in to comment.