Skip to content

Commit

Permalink
feat: implement logger adapter (#67)
Browse files Browse the repository at this point in the history
* feat: implement logger adapter

* improve test coverage
  • Loading branch information
reugn authored Sep 15, 2023
1 parent 6c23d5c commit 1fb8849
Show file tree
Hide file tree
Showing 8 changed files with 408 additions and 6 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,19 @@ Implemented Jobs
| Day of week | YES | 1-7 or SUN-SAT | , - * ? / |
| Year | NO | empty, 1970- | , - * / |

## Logger

To set a custom logger, use the `logger.SetDefault` function.
The argument must implement the `logger.Logger` interface.

The following example shows how to disable library logs.

```go
import "github.com/reugn/go-quartz/quartz/logger"

logger.SetDefault(logger.NewSimpleLogger(nil, logger.LevelOff))
```

## Examples

```go
Expand Down
3 changes: 3 additions & 0 deletions quartz/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"strings"
"sync"
"sync/atomic"

"github.com/reugn/go-quartz/quartz/logger"
)

// Job represents an interface to be implemented by structs which represent a 'job'
Expand Down Expand Up @@ -188,6 +190,7 @@ type isolatedJob struct {
// Execute is called by a Scheduler when the Trigger associated with this job fires.
func (j *isolatedJob) Execute(ctx context.Context) {
if wasRunning := j.isRunning.Swap(true); wasRunning != nil && wasRunning.(bool) {
logger.Debugf("Executed job %d is running.", j.Job.Key())
return
}
defer j.isRunning.Store(false)
Expand Down
80 changes: 80 additions & 0 deletions quartz/logger/default_logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package logger

import (
"log"
"os"
"sync/atomic"
)

var defaultLogger atomic.Value

func init() {
defaultLogger.Store(NewSimpleLogger(
log.New(os.Stdout, "", log.LstdFlags|log.Lshortfile), LevelInfo),
)
}

// Default returns the default Logger.
func Default() Logger {
return defaultLogger.Load().(Logger)
}

// SetDefault makes l the default Logger.
func SetDefault(l Logger) {
defaultLogger.Store(l)
}

// Trace logs at LevelTrace.
func Trace(msg any) {
Default().Trace(msg)
}

// Tracef logs at LevelTrace.
func Tracef(format string, args ...any) {
Default().Tracef(format, args...)
}

// Debug logs at LevelDebug.
func Debug(msg any) {
Default().Debug(msg)
}

// Debugf logs at LevelDebug.
func Debugf(format string, args ...any) {
Default().Debugf(format, args...)
}

// Info logs at LevelInfo.
func Info(msg any) {
Default().Info(msg)
}

// Infof logs at LevelInfo.
func Infof(format string, args ...any) {
Default().Infof(format, args...)
}

// Warn logs at LevelWarn.
func Warn(msg any) {
Default().Warn(msg)
}

// Warnf logs at LevelWarn.
func Warnf(format string, args ...any) {
Default().Warnf(format, args...)
}

// Error logs at LevelError.
func Error(msg any) {
Default().Error(msg)
}

// Errorf logs at LevelError.
func Errorf(format string, args ...any) {
Default().Errorf(format, args...)
}

// Enabled reports whether the logger handles records at the given level.
func Enabled(level Level) bool {
return Default().Enabled(level)
}
15 changes: 15 additions & 0 deletions quartz/logger/level.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package logger

// A Level is the importance or severity of a log event.
// The higher the level, the more important or severe the event.
type Level int

// Names for common log levels.
const (
LevelTrace Level = -8
LevelDebug Level = -4
LevelInfo Level = 0
LevelWarn Level = 4
LevelError Level = 8
LevelOff Level = 12
)
38 changes: 38 additions & 0 deletions quartz/logger/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package logger

// A Logger handles log records.
type Logger interface {

// Trace logs at LevelTrace.
Trace(msg any)

// Tracef logs at LevelTrace.
Tracef(format string, args ...any)

// Debug logs at LevelDebug.
Debug(msg any)

// Debugf logs at LevelDebug.
Debugf(format string, args ...any)

// Info logs at LevelInfo.
Info(msg any)

// Infof logs at LevelInfo.
Infof(format string, args ...any)

// Warn logs at LevelWarn.
Warn(msg any)

// Warnf logs at LevelWarn.
Warnf(format string, args ...any)

// Error logs at LevelError.
Error(msg any)

// Errorf logs at LevelError.
Errorf(format string, args ...any)

// Enabled reports whether the logger handles records at the given level.
Enabled(level Level) bool
}
122 changes: 122 additions & 0 deletions quartz/logger/logger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package logger_test

import (
"bytes"
"io"
"log"
"strings"
"testing"

"github.com/reugn/go-quartz/quartz/logger"
)

func TestSimpleLogger(t *testing.T) {
var b bytes.Buffer
stdLogger := log.New(&b, "", log.LstdFlags)
logger.SetDefault(logger.NewSimpleLogger(stdLogger, logger.LevelInfo))

logger.Trace("Trace")
assertEmpty(&b, t)
logger.Tracef("Trace%s", "f")
assertEmpty(&b, t)

logger.Debug("Debug")
assertEmpty(&b, t)
logger.Debugf("Debug%s", "f")
assertEmpty(&b, t)

logger.Info("Info")
assertNotEmpty(&b, t)
logger.Infof("Info%s", "f")
assertNotEmpty(&b, t)

logger.Warn("Warn")
assertNotEmpty(&b, t)
logger.Warnf("Warn%s", "f")
assertNotEmpty(&b, t)

logger.Error("Error")
assertNotEmpty(&b, t)
logger.Errorf("Error%s", "f")
assertNotEmpty(&b, t)
}

func TestLoggerOff(t *testing.T) {
var b bytes.Buffer
stdLogger := log.New(&b, "", log.LstdFlags)
logger.SetDefault(logger.NewSimpleLogger(stdLogger, logger.LevelOff))

if logger.Enabled(logger.LevelError) {
t.Fatal("LevelError is enabled")
}
logger.Error("Error")
assertEmpty(&b, t)
logger.Errorf("Error%s", "f")
assertEmpty(&b, t)
}

func TestLogFormat(t *testing.T) {
var b bytes.Buffer
stdLogger := log.New(&b, "", log.LstdFlags)
logr := logger.NewSimpleLogger(stdLogger, logger.LevelTrace)
logger.SetDefault(logr)

empty := struct{}{}
logr.Trace("Trace")
assertNotEmpty(&b, t)
logr.Tracef("Tracef: %s, %d, %v, %v", "a", 1, true, empty)
checkLogFormat(&b, t)
logger.Tracef("Tracef: %s, %d, %v, %v", "a", 1, true, empty)
checkLogFormat(&b, t)

logr.Debug("Debug")
assertNotEmpty(&b, t)
logr.Debugf("Debugf: %s, %d, %v, %v", "a", 1, true, empty)
checkLogFormat(&b, t)
logger.Debugf("Debugf: %s, %d, %v, %v", "a", 1, true, empty)
checkLogFormat(&b, t)

logr.Infof("Infof: %s, %d, %v, %v", "a", 1, true, empty)
checkLogFormat(&b, t)
logger.Infof("Infof: %s, %d, %v, %v", "a", 1, true, empty)
checkLogFormat(&b, t)

logr.Warnf("Warnf: %s, %d, %v, %v", "a", 1, true, empty)
checkLogFormat(&b, t)
logger.Warnf("Warnf: %s, %d, %v, %v", "a", 1, true, empty)
checkLogFormat(&b, t)

logr.Errorf("Errorf: %s, %d, %v, %v", "a", 1, true, empty)
checkLogFormat(&b, t)
logger.Errorf("Errorf: %s, %d, %v, %v", "a", 1, true, empty)
checkLogFormat(&b, t)
}

func assertEmpty(r io.Reader, t *testing.T) {
logMsg := readAll(r, t)
if logMsg != "" {
t.Fatalf("Log msg is not empty: %s", logMsg)
}
}

func assertNotEmpty(r io.Reader, t *testing.T) {
logMsg := readAll(r, t)
if logMsg == "" {
t.Fatal("Log msg is empty")
}
}

func checkLogFormat(r io.Reader, t *testing.T) {
logMsg := readAll(r, t)
if !strings.Contains(logMsg, "a, 1, true, {}") {
t.Fatalf("Invalid log format: %s", logMsg)
}
}

func readAll(r io.Reader, t *testing.T) string {
bytes, err := io.ReadAll(r)
if err != nil {
t.Fatal(err)
}
return string(bytes)
}
Loading

0 comments on commit 1fb8849

Please sign in to comment.