Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement logger adapter #67

Merged
merged 2 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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