Skip to content

Commit

Permalink
Merge pull request #116 from suecodelabs/bug/logger-fatal
Browse files Browse the repository at this point in the history
fixed bug in logger.Fatal*() and added testcases
  • Loading branch information
Luukvdm authored Oct 28, 2022
2 parents ba559dc + ec18ef2 commit 9e89116
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 5 deletions.
61 changes: 61 additions & 0 deletions src/logger/exit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2022 Sue B.V.
*
* 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 logger

// Inspired by
// https://stackoverflow.com/a/30690532

import "os"

// Func takes a code as exit status
type Func func(int)

// Exit has an exit func, and will memorize the exit status code
type Exit struct {
exit Func
status int
}

// Exit calls the exiter, and then returns code as status.
// If e was declared, but never set (since only a test would set e),
// simply calls os.Exit()
func (e *Exit) Exit(code int) {
if e != nil {
e.status = code
e.exit(code)
} else {
os.Exit(code)
}
}

// Status get the exit status code as memorized
// after the call to the exit func.
func (e *Exit) Status() int {
return e.status
}

// Default returns an Exit with default os.Exit() call.
// That means the status will never be visible,
// since os.Exit() stops everything.
func Default() *Exit {
return &Exit{exit: os.Exit}
}

// CreateExiter returns an exiter with a custom function
func CreateExiter(exit Func) *Exit {
return &Exit{exit: exit}
}
30 changes: 26 additions & 4 deletions src/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"log"
"os"
)

const (
Expand All @@ -34,22 +33,45 @@ const (

type Logger struct {
logr.Logger
exiter *Exit
}

// CreateLogger creates a new logger instance
// isDebug: if enabled, the logger prints debug logs, otherwise it prints info level and above
func CreateLogger(isDebug bool) Logger {
var logger logr.Logger
logger = zapr.NewLogger(createZapLogger(isDebug))
// TODO
return Logger{
logger,
nil,
}
}

// SetExiter useful for debugging
// when the exit is set, the code won't call os.Exit($code) but will set the status on the Exit struct
// this is useful when you want to test if a function crashes with os.Exit(1) for example.
func (l Logger) SetExiter(exit *Exit) {
l.exiter = exit
}

// Fatal logs message with important level and exits with code 1
func (l Logger) Fatal(msg string, keysAndValues ...interface{}) {
if keysAndValues == nil {
l.V(ImportantLevel).Info(msg)
} else {
l.V(ImportantLevel).Info(msg, keysAndValues)
}
l.exiter.Exit(1)
}

// FatalError logs message and error with important level and exits with code 1
func (l Logger) FatalError(err error, msg string, keysAndValues ...interface{}) {
l.V(ImportantLevel).Error(err, msg, keysAndValues)
os.Exit(1)
if keysAndValues == nil {
l.V(ImportantLevel).Error(err, msg)
} else {
l.V(ImportantLevel).Error(err, msg, keysAndValues)
}
l.exiter.Exit(1)
}

func createZapLogger(isDebug bool) *zap.Logger {
Expand Down
44 changes: 44 additions & 0 deletions src/logger/logger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2022 Sue B.V.
*
* 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 logger

import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)

func TestFatalError(t *testing.T) {
l := CreateDebugLogger()
err := fmt.Errorf("test error")
msg := "this is a test crash"
l.FatalError(err, msg)
assert.Equal(t, 1, l.GetExiter().status)
logs := GetObservedLogs()
theMsg := logs.FilterMessage(msg)
assert.Equal(t, theMsg.All()[0].Message, msg)
}

func TestFatal(t *testing.T) {
l := CreateDebugLogger()
msg := "this is a test crash"
l.Fatal(msg)
assert.Equal(t, 1, l.GetExiter().status)
logs := GetObservedLogs()
theMsg := logs.FilterMessage(msg)
assert.Equal(t, theMsg.All()[0].Message, msg)
}
10 changes: 9 additions & 1 deletion src/logger/test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,22 @@ var observedLogs *observer.ObservedLogs

func CreateDebugLogger() Logger {
l := zapr.NewLogger(createObservedZapLogger())
return Logger{l}
e := CreateExiter(func(i int) {})
return Logger{
l,
e,
}
}

// GetObservedLogs returns ObservedLogs object, can be used in unit tests to see if something got logged
func GetObservedLogs() *observer.ObservedLogs {
return observedLogs
}

func (l Logger) GetExiter() *Exit {
return l.exiter
}

func createObservedZapLogger() *zap.Logger {
core, logs := observer.New(zapcore.Level(PerformanceTestLevel)) // TODO implement variable info level
zLogger := zap.New(core)
Expand Down

0 comments on commit 9e89116

Please sign in to comment.