Skip to content

Commit

Permalink
Merge pull request #33 from dl1998/add-async-structuredlogger
Browse files Browse the repository at this point in the history
Add async structuredlogger
  • Loading branch information
dl1998 authored Apr 1, 2024
2 parents ce107c5 + 189d303 commit fdd3889
Show file tree
Hide file tree
Showing 14 changed files with 2,278 additions and 1,671 deletions.
25 changes: 25 additions & 0 deletions docs/architecture/diagrams/plantuml/class_diagram.plantuml
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,28 @@ package pkg {
+ Critical(parameters : ...any)
+ Emergency(parameters : ...any)
}
struct baseAsyncLogger implements baseLoggerInterface {
~ *baseLogger
~ messageQueue : chan logrecord.Interface
~ waitGroup sync.WaitGroup
~ startListeningMessages()
+ WaitToFinishLogging()
+ Open(queueSize : int)
+ Close()
+ Log(level : level.Level, parameters : ...any)
}
interface AsyncLoggerInterface extends Interface {
+ Interface
+ WaitToFinishLogging()
+ Open(queueSize : int)
+ Close()
}
struct AsyncLogger implements AsyncLoggerInterface {
+ *Logger
+ WaitToFinishLogging()
+ Open(queueSize : int)
+ Close()
}
struct Configuration {
~ fromLevel : level.Level
~ toLevel : level.Level
Expand All @@ -475,6 +497,7 @@ package pkg {
~ template : map[string]string
~ init()
+ New(name : string, timeFormat : string) : *Logger
+ NewAsyncLogger(name : string, timeFormat : string, queueSize : int) : *AsyncLogger
+ WithFromLevel(fromLevel : level.Level) : Option
+ WithToLevel(toLevel : level.Level) : Option
+ WithTemplate(template : map[string]string) : Option
Expand Down Expand Up @@ -504,7 +527,9 @@ package pkg {
+ Emergency(message : string, parameters : ...any)
}

baseAsyncLogger *-- baseLogger
Logger *-- baseLoggerInterface
AsyncLogger *-- Logger
"<<module>>" ..> Logger : uses
"<<module>>" ..> Option : uses
"<<module>>" ..> Configuration : uses
Expand Down
Binary file modified docs/architecture/diagrams/png/class_diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2,525 changes: 1,342 additions & 1,183 deletions docs/architecture/diagrams/svg/class_diagram.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ Demonstrates how to create a new custom logger for the application.
It creates an async logger with provided queue size that writes messages on the console asynchronously. It also provides
an example how to wait for all messages to be written.

## customasyncstructuredlogger

Demonstrates how to create a new custom structured logger for the application.

It creates an async structured logger with provided queue size that writes messages on the console asynchronously. It
also provides an example how to wait for all messages to be written.

## customlogger

Demonstrates how to create a new custom logger for the application.
Expand Down
33 changes: 33 additions & 0 deletions examples/customasyncstructuredlogger/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Example that shows how to create and use custom async structured logger.
package main

import (
"fmt"
"github.com/dl1998/go-logging/pkg/common/level"
"github.com/dl1998/go-logging/pkg/structuredlogger"
"github.com/dl1998/go-logging/pkg/structuredlogger/formatter"
"github.com/dl1998/go-logging/pkg/structuredlogger/handler"
"time"
)

func main() {
asyncQueueSize := 10

applicationLogger := structuredlogger.NewAsyncLogger("example", time.DateTime, asyncQueueSize)

applicationFormatter := formatter.NewJSON(map[string]string{
"time": "%(datetime)",
"level": "%(level)",
"name": "%(name)",
}, false)
consoleHandler := handler.NewConsoleHandler(level.Debug, level.Null, applicationFormatter)
applicationLogger.AddHandler(consoleHandler)

for index := 0; index < asyncQueueSize; index++ {
applicationLogger.Warning("message", "This message will be displayed.")
}

fmt.Println("This will be printed before the last warning log message.")

applicationLogger.WaitToFinishLogging()
}
47 changes: 25 additions & 22 deletions pkg/logger/asynclogger_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package logger

import (
"fmt"
"github.com/dl1998/go-logging/internal/testutils"
"github.com/dl1998/go-logging/pkg/common/level"
"github.com/dl1998/go-logging/pkg/logger/handler"
"github.com/dl1998/go-logging/pkg/logger/logrecord"
"sync"
Expand All @@ -27,7 +27,7 @@ func TestBaseAsyncLogger_startListeningMessages(t *testing.T) {
waitGroup: sync.WaitGroup{},
}

record := logrecord.New(loggerName, 0, timeFormat, message, parameters, 3)
record := logrecord.New(loggerName, logLevel, timeFormat, message, parameters, skipCallers)
newBaseAsyncLogger.messageQueue <- record

go newBaseAsyncLogger.startListeningMessages()
Expand All @@ -37,8 +37,9 @@ func TestBaseAsyncLogger_startListeningMessages(t *testing.T) {
testutils.AssertEquals(t, record, mockHandler.Parameters[0].(*logrecord.LogRecord))
}

// TestBaseAsyncLogger_WaitToFinishLogging tests
// baseAsyncLogger.WaitToFinishLogging method of the baseAsyncLogger.
// TestBaseAsyncLogger_WaitToFinishLogging tests that
// baseAsyncLogger.WaitToFinishLogging waits until async logger finish logging
// messages.
func TestBaseAsyncLogger_WaitToFinishLogging(t *testing.T) {
mockHandler := &MockHandler{}
newBaseAsyncLogger := &baseAsyncLogger{
Expand All @@ -63,8 +64,8 @@ func TestBaseAsyncLogger_WaitToFinishLogging(t *testing.T) {
testutils.AssertEquals(t, true, waited)
}

// TestBaseAsyncLogger_Open tests baseAsyncLogger.Open method of the
// baseAsyncLogger.
// TestBaseAsyncLogger_Open tests that baseAsyncLogger.Open creates a new message
// queue and start listening messages.
func TestBaseAsyncLogger_Open(t *testing.T) {
mockHandler := &MockHandler{}
newBaseAsyncLogger := &baseAsyncLogger{
Expand All @@ -91,8 +92,8 @@ func isChannelClosed(ch <-chan logrecord.Interface) bool {
}
}

// TestBaseAsyncLogger_Close tests baseAsyncLogger.Close method of the
// baseAsyncLogger.
// TestBaseAsyncLogger_Close tests that baseAsyncLogger.Close closes message
// queue channel.
func TestBaseAsyncLogger_Close(t *testing.T) {
mockHandler := &MockHandler{}
newBaseAsyncLogger := &baseAsyncLogger{
Expand All @@ -109,7 +110,8 @@ func TestBaseAsyncLogger_Close(t *testing.T) {
testutils.AssertEquals(t, true, isChannelClosed(newBaseAsyncLogger.messageQueue))
}

// TestBaseAsyncLogger_Log tests baseAsyncLogger.Log method of the baseAsyncLogger.
// TestBaseAsyncLogger_Log tests that baseAsyncLogger.Log sends a new record on
// the message queue.
func TestBaseAsyncLogger_Log(t *testing.T) {
mockHandler := &MockHandler{}
newBaseAsyncLogger := &baseAsyncLogger{
Expand All @@ -121,12 +123,11 @@ func TestBaseAsyncLogger_Log(t *testing.T) {
waitGroup: sync.WaitGroup{},
}

expectedMessage := "test"
newBaseAsyncLogger.Log(0, expectedMessage)
actualMessage, ok := <-newBaseAsyncLogger.messageQueue
newBaseAsyncLogger.Log(logLevel, message, parameters...)
record := <-newBaseAsyncLogger.messageQueue

testutils.AssertEquals(t, true, ok)
testutils.AssertEquals(t, expectedMessage, actualMessage.Message())
testutils.AssertEquals(t, logLevel, record.Level())
testutils.AssertEquals(t, fmt.Sprintf(message, parameters...), record.Message())
}

// BenchmarkBaseAsyncLogger_Log benchmarks baseAsyncLogger.Log method of the
Expand All @@ -145,11 +146,12 @@ func BenchmarkBaseAsyncLogger_Log(b *testing.B) {
b.ResetTimer()

for index := 0; index < b.N; index++ {
newBaseAsyncLogger.Log(level.Trace, message, parameters...)
newBaseAsyncLogger.Log(logLevel, message, parameters...)
}
}

// TestNewAsyncLogger tests NewAsyncLogger function of the baseAsyncLogger.
// TestNewAsyncLogger tests that NewAsyncLogger creates and return a new
// AsyncLogger.
func TestNewAsyncLogger(t *testing.T) {
newAsyncLogger := NewAsyncLogger(loggerName, timeFormat, messageQueueSize)

Expand All @@ -158,16 +160,15 @@ func TestNewAsyncLogger(t *testing.T) {
testutils.AssertEquals(t, messageQueueSize, cap(newAsyncLogger.baseLogger.(*baseAsyncLogger).messageQueue))
}

// BenchmarkNewAsyncLogger benchmarks NewAsyncLogger function of the
// baseAsyncLogger.
// BenchmarkNewAsyncLogger benchmarks NewAsyncLogger.
func BenchmarkNewAsyncLogger(b *testing.B) {
for index := 0; index < b.N; index++ {
NewAsyncLogger(loggerName, timeFormat, messageQueueSize)
}
}

// TestAsyncLogger_WaitToFinishLogging tests AsyncLogger.WaitToFinishLogging
// method of the AsyncLogger.
// TestAsyncLogger_WaitToFinishLogging tests that AsyncLogger.WaitToFinishLogging
// waits until async logger finish logging messages.
func TestAsyncLogger_WaitToFinishLogging(t *testing.T) {
mockHandler := &MockHandler{}
newAsyncLogger := &AsyncLogger{
Expand Down Expand Up @@ -195,7 +196,8 @@ func TestAsyncLogger_WaitToFinishLogging(t *testing.T) {
testutils.AssertEquals(t, true, waited)
}

// TestAsyncLogger_Open tests AsyncLogger.Open method of the AsyncLogger.
// TestAsyncLogger_Open tests that AsyncLogger.Open creates a new message queue
// and start listening messages.
func TestAsyncLogger_Open(t *testing.T) {
mockHandler := &MockHandler{}
newAsyncLogger := &AsyncLogger{
Expand All @@ -216,7 +218,8 @@ func TestAsyncLogger_Open(t *testing.T) {
testutils.AssertNotNil(t, newAsyncLogger.baseLogger.(*baseAsyncLogger).messageQueue)
}

// TestAsyncLogger_Close tests AsyncLogger.Close method of the AsyncLogger.
// TestAsyncLogger_Close tests that AsyncLogger.Close closes message queue
// channel.
func TestAsyncLogger_Close(t *testing.T) {
mockHandler := &MockHandler{}
newAsyncLogger := &AsyncLogger{
Expand Down
12 changes: 5 additions & 7 deletions pkg/logger/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import (
var (
loggerTemplate = "%(level):%(name):%(message)"
loggerName = "test"
logLevel = level.Debug
message = "Test Message: %s."
parameters = []any{
"test",
}
timeFormat = time.RFC3339
timeFormat = time.RFC3339
skipCallers = 3
)

// MockLogger is used to mock baseLogger.
Expand Down Expand Up @@ -110,7 +112,7 @@ func (mock *MockHandler) FromLevel() level.Level {
mock.CalledName = "FromLevel"
mock.Called = true
mock.Parameters = make([]any, 0)
returnValue := level.Debug
returnValue := logLevel
mock.Return = returnValue
return returnValue
}
Expand All @@ -128,7 +130,7 @@ func (mock *MockHandler) ToLevel() level.Level {
mock.CalledName = "ToLevel"
mock.Called = true
mock.Parameters = make([]any, 0)
returnValue := level.Debug
returnValue := logLevel
mock.Return = returnValue
return returnValue
}
Expand Down Expand Up @@ -170,8 +172,6 @@ func TestBaseLogger_Log(t *testing.T) {
},
}

logLevel := level.Debug

newBaseLogger.Log(logLevel, message, parameters...)

handlerRecord := newHandler.Parameters[0].(*logrecord.LogRecord)
Expand All @@ -190,8 +190,6 @@ func BenchmarkBaseLogger_Log(b *testing.B) {
},
}

logLevel := level.Debug

for index := 0; index < b.N; index++ {
newBaseLogger.Log(logLevel, message, parameters...)
}
Expand Down
12 changes: 6 additions & 6 deletions pkg/logger/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,18 +406,18 @@ func BenchmarkLogger_Emergency(b *testing.B) {
func TestWithFromLevel(t *testing.T) {
configuration := NewConfiguration()

option := WithFromLevel(level.Trace)
option := WithFromLevel(logLevel)

option(configuration)

testutils.AssertEquals(t, level.Trace, configuration.fromLevel)
testutils.AssertEquals(t, logLevel, configuration.fromLevel)
}

// BenchmarkWithFromLevel perform benchmarking of the WithFromLevel().
func BenchmarkWithFromLevel(b *testing.B) {
configuration := NewConfiguration()

option := WithFromLevel(level.Trace)
option := WithFromLevel(logLevel)

for index := 0; index < b.N; index++ {
option(configuration)
Expand All @@ -428,18 +428,18 @@ func BenchmarkWithFromLevel(b *testing.B) {
func TestWithToLevel(t *testing.T) {
configuration := NewConfiguration()

option := WithToLevel(level.Trace)
option := WithToLevel(logLevel)

option(configuration)

testutils.AssertEquals(t, level.Trace, configuration.toLevel)
testutils.AssertEquals(t, logLevel, configuration.toLevel)
}

// BenchmarkWithToLevel perform benchmarking of the WithToLevel().
func BenchmarkWithToLevel(b *testing.B) {
configuration := NewConfiguration()

option := WithToLevel(level.Trace)
option := WithToLevel(logLevel)

for index := 0; index < b.N; index++ {
option(configuration)
Expand Down
Loading

0 comments on commit fdd3889

Please sign in to comment.