Skip to content

Commit

Permalink
Replace variables with LogRecord
Browse files Browse the repository at this point in the history
  • Loading branch information
dl1998 committed Mar 22, 2024
1 parent ab06058 commit b5dad5e
Show file tree
Hide file tree
Showing 18 changed files with 522 additions and 178 deletions.
42 changes: 24 additions & 18 deletions pkg/common/formatter/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,31 @@
package formatter

import (
"github.com/dl1998/go-logging/pkg/common/level"
"runtime"
"time"
"github.com/dl1998/go-logging/pkg/common/logrecord"
)

// EvaluatePreset evaluates pre-defined set of formatting options and returns map
// with mapping of the option to interpolated value.
func EvaluatePreset(loggerName string, logLevel level.Level, skipCaller int) map[string]interface{} {
_, functionName, functionLine, _ := runtime.Caller(skipCaller)
var presets = map[string]interface{}{
"%(name)": loggerName, // Logger name
"%(time)": time.Now().Format(time.TimeOnly), // Current time (format: HH:MM:ss)
"%(date)": time.Now().Format(time.DateOnly), // Current date (format: yyyy-mm-dd)
"%(isotime)": time.Now().Format(time.RFC3339), // Current date and time (format: yyyy-mm-ddTHH:MM:ssGMT)
"%(timestamp)": time.Now().Unix(), // Current timestamp
"%(level)": logLevel.String(), // Logging log level name
"%(levelnr)": logLevel.DigitRepresentation(), // Logging log level number
"%(fname)": functionName, // Name of the function from which logger has been called
"%(fline)": functionLine, // Line number from which logger has been called
// ParseKey parses the key and returns the value.
func ParseKey(key string, record logrecord.Interface) interface{} {
var value interface{}

switch key {
case "%(name)":
value = record.Name()
case "%(level)":
value = record.Level().String()
case "%(levelnr)":
value = record.Level().DigitRepresentation()
case "%(datetime)":
value = record.Time()
case "%(timestamp)":
value = record.Timestamp()
case "%(fname)":
value = record.FileName()
case "%(fline)":
value = record.FileLine()
default:
value = key
}
return presets

return value
}
68 changes: 56 additions & 12 deletions pkg/common/formatter/formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,68 @@ package formatter
import (
"github.com/dl1998/go-logging/internal/testutils"
"github.com/dl1998/go-logging/pkg/common/level"
"github.com/dl1998/go-logging/pkg/common/logrecord"
"testing"
)

var loggerName = "test"
var loggingLevel = level.Debug
var (
loggerName = "test"
loggingLevel = level.Debug
timeFormat = ""
skipCaller = 1
)

// TestParseKey tests that ParseKey returns correct value for the key.
func TestParseKey(t *testing.T) {
record := logrecord.New(loggerName, loggingLevel, timeFormat, skipCaller)

// TestFormatter_EvaluatePreset tests that Formatter.EvaluatePreset correctly
// evaluates tags.
func TestEvaluatePreset(t *testing.T) {
preset := EvaluatePreset(loggerName, loggingLevel, 1)
tests := map[string]struct {
key string
expected interface{}
}{
"Name": {key: "%(name)", expected: loggerName},
"Level name": {key: "%(level)", expected: loggingLevel.String()},
"Level number": {key: "%(levelnr)", expected: loggingLevel.DigitRepresentation()},
"Date time": {key: "%(datetime)", expected: record.Time()},
"Timestamp": {key: "%(timestamp)", expected: record.Timestamp()},
"Function name": {key: "%(fname)", expected: record.FileName()},
"Function line": {key: "%(fline)", expected: record.FileLine()},
"Not a key": {key: "not a key", expected: "not a key"},
}

testutils.AssertEquals(t, loggerName, preset["%(name)"].(string))
testutils.AssertEquals(t, loggingLevel.String(), preset["%(level)"].(string))
for name, test := range tests {
t.Run(name, func(t *testing.T) {
value := ParseKey(test.key, record)

testutils.AssertEquals(t, test.expected, value)
})
}
}

// BenchmarkFormatter_EvaluatePreset performs benchmarking of the Formatter.EvaluatePreset().
func BenchmarkEvaluatePreset(b *testing.B) {
for index := 0; index < b.N; index++ {
EvaluatePreset(loggerName, loggingLevel, 1)
// BenchmarkParseKey performs benchmarking of the ParseKey().
func BenchmarkParseKey(b *testing.B) {
record := logrecord.New(loggerName, loggingLevel, timeFormat, skipCaller)

benchmarks := map[string]struct {
key string
}{
"Name": {key: "%(name)"},
"Level name": {key: "%(level)"},
"Level number": {key: "%(levelnr)"},
"Date time": {key: "%(datetime)"},
"Timestamp": {key: "%(timestamp)"},
"Function name": {key: "%(fname)"},
"Function line": {key: "%(fline)"},
"Not a key": {key: "not a key"},
}

for name, benchmark := range benchmarks {
b.Run(name, func(b *testing.B) {
b.ResetTimer()

for index := 0; index < b.N; index++ {
ParseKey(benchmark.key, record)
}
})
}
}
31 changes: 16 additions & 15 deletions pkg/common/level/level.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,24 @@ const (
Null
)

var mapping = map[Level]string{
All: "all",
Trace: "trace",
Debug: "debug",
Verbose: "verbose",
Info: "info",
Notice: "notice",
Warning: "warning",
Severe: "severe",
Error: "error",
Alert: "alert",
Critical: "critical",
Emergency: "emergency",
Null: "null",
}

// String returns string representation of the Level.
func (level Level) String() string {
mapping := map[Level]string{
All: "all",
Trace: "trace",
Debug: "debug",
Verbose: "verbose",
Info: "info",
Notice: "notice",
Warning: "warning",
Severe: "severe",
Error: "error",
Alert: "alert",
Critical: "critical",
Emergency: "emergency",
Null: "null",
}
return mapping[level]
}

Expand Down
9 changes: 9 additions & 0 deletions pkg/common/logrecord/logrecord.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ import (
"time"
)

type Interface interface {
Name() string
Time() string
Timestamp() int64
Level() level.Level
FileName() string
FileLine() int
}

// LogRecord struct represents a log record.
type LogRecord struct {
// Name of the logger.
Expand Down
64 changes: 43 additions & 21 deletions pkg/logger/formatter/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package formatter
import (
commonformatter "github.com/dl1998/go-logging/pkg/common/formatter"
"github.com/dl1998/go-logging/pkg/common/level"
"github.com/dl1998/go-logging/pkg/logger/logrecord"
"strconv"
"strings"
)
Expand All @@ -30,7 +31,7 @@ const resetColor = "\033[0m"
// Interface represents interface that shall be satisfied by Formatter.
type Interface interface {
Template() string
Format(message string, loggerName string, logLevel level.Level, colored bool) string
Format(record *logrecord.LogRecord, colored bool) string
}

// Formatter struct that contains necessary for the formatting fields.
Expand All @@ -55,30 +56,51 @@ func (formatter *Formatter) Template() string {
}

// Format formats provided message template to the interpolated string.
func (formatter *Formatter) Format(message string, loggerName string, logLevel level.Level, colored bool) string {
var presets = commonformatter.EvaluatePreset(loggerName, logLevel, 2)
var convertedPresets = make(map[string]string, len(presets)+1)
for key, value := range presets {
switch convertedValue := value.(type) {
case string:
convertedPresets[key] = convertedValue
case int:
convertedPresets[key] = strconv.Itoa(convertedValue)
case int64:
convertedPresets[key] = strconv.FormatInt(convertedValue, 10)
}
func (formatter *Formatter) Format(record *logrecord.LogRecord, colored bool) string {
format := ParseTemplate(formatter.template, record)

if colored {
format = logLevelColors[record.Level()] + format + resetColor
}
convertedPresets["%(message)"] = message

format := formatter.template
return format + "\n"
}

for key, value := range convertedPresets {
format = strings.ReplaceAll(format, key, value)
}
func ParseTemplate(format string, record logrecord.Interface) string {
format = ReplaceKey(format, "%(name)", record)
format = ReplaceKey(format, "%(level)", record)
format = ReplaceKey(format, "%(levelnr)", record)
format = ReplaceKey(format, "%(datetime)", record)
format = ReplaceKey(format, "%(timestamp)", record)
format = ReplaceKey(format, "%(fname)", record)
format = ReplaceKey(format, "%(fline)", record)
format = ReplaceKey(format, "%(message)", record)
return format
}

if colored {
format = logLevelColors[logLevel] + format + resetColor
func ReplaceKey(format string, key string, record logrecord.Interface) string {
if strings.Contains(format, key) {
value := ParseKey(key, record)
return strings.ReplaceAll(format, key, value)
}
return format
}

return format + "\n"
func ParseKey(key string, record logrecord.Interface) string {
switch key {
case "%(message)":
return record.Message()
default:
value := commonformatter.ParseKey(key, record)
switch convertedValue := value.(type) {
case int64:
return strconv.FormatInt(convertedValue, 10)
case int:
return strconv.Itoa(convertedValue)
case string:
return convertedValue
default:
return value.(string)
}
}
}
Loading

0 comments on commit b5dad5e

Please sign in to comment.