Skip to content

Commit

Permalink
V8.0.0 rc7 (#45)
Browse files Browse the repository at this point in the history
- Removed ability to remove individual log levels. Can use AddHandler
  and RemoveHandler to accomplish the same. Will leave it this way
  unless there's a compelling reason to expose this functionality.
- Move std log redirect to top level our of console handler.
- Add error and warning checks for std log output
- fix std log redirect reset
  • Loading branch information
Dean Karn authored Jun 3, 2022
1 parent 77e35e4 commit d55926d
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 68 deletions.
7 changes: 7 additions & 0 deletions _examples/basic/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package main

import (
"io"
stdlog "log"

"github.com/go-playground/errors/v5"
"github.com/go-playground/log/v8"
)

func main() {
log.RedirectGoStdLog(true)

// Trace
defer log.WithTrace().Info("time to run")

Expand Down Expand Up @@ -44,4 +47,8 @@ func main() {
)

logger.WithField("key", "value").Info("test")

stdlog.Println("This was redirected from Go STD output!")
log.RedirectGoStdLog(false)
stdlog.Println("This was NOT redirected from Go STD output!")
}
2 changes: 1 addition & 1 deletion benchmarks/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var _jane = user{
// annoying because you have to manipulate the TestMain before
// running the benchmark you want.
func TestMain(m *testing.M) {
cLog := log.NewConsoleBuilder().WithGoSTDErrLogs(false).WithWriter(ioutil.Discard).Build()
cLog := log.NewConsoleBuilder().WithWriter(ioutil.Discard).Build()
log.AddHandler(cLog, log.AllLevels...)
os.Exit(m.Run())
}
Expand Down
59 changes: 1 addition & 58 deletions default_logger.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package log

import (
"bufio"
"fmt"
"io"
stdlog "log"
"os"
"strconv"
"sync"
Expand All @@ -22,23 +20,16 @@ const (
type ConsoleBuilder struct {
writer io.Writer
timestampFormat string
redirect bool
}

// NewConsoleBuilder creates a new ConsoleBuilder for configuring and creating a new console logger
func NewConsoleBuilder() *ConsoleBuilder {
return &ConsoleBuilder{
writer: os.Stderr,
timestampFormat: DefaultTimeFormat,
redirect: true,
}
}

func (b *ConsoleBuilder) WithGoSTDErrLogs(redirect bool) *ConsoleBuilder {
b.redirect = redirect
return b
}

func (b *ConsoleBuilder) WithWriter(writer io.Writer) *ConsoleBuilder {
b.writer = writer
return b
Expand All @@ -50,44 +41,19 @@ func (b *ConsoleBuilder) WithTimestampFormat(format string) *ConsoleBuilder {
}

func (b *ConsoleBuilder) Build() *Logger {
c := &Logger{
return &Logger{
writer: b.writer,
timestampFormat: b.timestampFormat,
}
if b.redirect {
ready := make(chan struct{})
go c.handleStdLogger(ready)
<-ready // have to wait, it was running too quickly and some messages can be lost
}
return c
}

// Logger is an instance of the console logger
type Logger struct {
m sync.Mutex
writer io.Writer
r *io.PipeReader
timestampFormat string
}

// this will redirect the output of
func (c *Logger) handleStdLogger(ready chan<- struct{}) {
var w *io.PipeWriter
c.r, w = io.Pipe()
stdlog.SetOutput(w)

scanner := bufio.NewScanner(c.r)
go func() {
close(ready)
}()

for scanner.Scan() {
WithField("stdlog", true).Notice(scanner.Text())
}
_ = c.r.Close()
_ = w.Close()
}

// Log handles the log entry
func (c *Logger) Log(e Entry) {
var lvl string
Expand Down Expand Up @@ -152,26 +118,3 @@ func (c *Logger) Log(e Entry) {

BytePool().Put(buff)
}

// Close cleans up any resources and de-registers the handler with the logger
func (c *Logger) Close() error {
RemoveHandler(c)
// reset the output back to original
// since we reset the output prior to closing we don't have to wait
stdlog.SetOutput(os.Stderr)
if c.r != nil {
_ = c.r.Close()
}
return nil
}

func (c *Logger) closeAlreadyLocked() error {
removeHandler(c)
// reset the output back to original
// since we reset the output prior to closing we don't have to wait
stdlog.SetOutput(os.Stderr)
if c.r != nil {
_ = c.r.Close()
}
return nil
}
2 changes: 1 addition & 1 deletion default_logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ func TestConsoleLogger(t *testing.T) {

cLog := NewConsoleBuilder().WithWriter(buff).WithTimestampFormat("").Build()
AddHandler(cLog, AllLevels...)
defer func() { _ = cLog.Close() }()
for i, tt := range tests {

buff.Reset()
Expand Down Expand Up @@ -90,6 +89,7 @@ func TestConsoleLogger(t *testing.T) {

func TestConsoleSTDLogCapturing(t *testing.T) {
buff := new(buffer)
RedirectGoStdLog(true)
cLog := NewConsoleBuilder().WithWriter(buff).WithTimestampFormat("MST").Build()
AddHandler(cLog, AllLevels...)

Expand Down
61 changes: 57 additions & 4 deletions log.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package log

import (
"bufio"
"context"
"io"
stdlog "log"
"os"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -44,7 +48,9 @@ var (
}{
name: "log",
}
rw sync.RWMutex
rw sync.RWMutex
stdLogWriter *io.PipeWriter
redirectComplete chan struct{}
)

// Field is a single Field key and value
Expand All @@ -53,6 +59,54 @@ type Field struct {
Value interface{} `json:"value"`
}

// RedirectGoStdLog is used to redirect Go's internal std log output to this logger.
func RedirectGoStdLog(redirect bool) {
if (redirect && stdLogWriter != nil) || (!redirect && stdLogWriter == nil) {
// already redirected or already not redirected
return
}
if !redirect {
stdlog.SetOutput(os.Stderr)
// will stop scanner reading PipeReader
_ = stdLogWriter.Close()
stdLogWriter = nil
<-redirectComplete
return
}

ready := make(chan struct{})
redirectComplete = make(chan struct{})

// last option is to redirect
go func() {
var r *io.PipeReader
r, stdLogWriter = io.Pipe()
defer func() {
_ = r.Close()
}()

stdlog.SetOutput(stdLogWriter)
defer func() {
close(redirectComplete)
redirectComplete = nil
}()

scanner := bufio.NewScanner(r)
close(ready)
for scanner.Scan() {
txt := scanner.Text()
if strings.Contains(txt, "error") {
WithField("stdlog", true).Error(txt)
} else if strings.Contains(txt, "warning") {
WithField("stdlog", true).Warn(txt)
} else {
WithField("stdlog", true).Notice(txt)
}
}
}()
<-ready
}

// SetExitFunc sets the provided function as the exit function used in Fatal(),
// Fatalf(), Panic() and Panicf(). This is primarily used when wrapping this library,
// you can set this to enable testing (with coverage) of your Fatal() and Fatalf()
Expand Down Expand Up @@ -116,7 +170,6 @@ func AddHandler(h Handler, levels ...Level) {
defer rw.Unlock()
if defaultHandler != nil {
removeHandler(h)
_ = defaultHandler.closeAlreadyLocked()
defaultHandler = nil
}
for _, level := range levels {
Expand Down Expand Up @@ -149,9 +202,9 @@ OUTER:
}
}

// RemoveHandlerLevels removes the supplied levels, if no more levels exists for the handler
// removeHandlerLevels removes the supplied levels, if no more levels exists for the handler
// it will no longer be registered and need to added via AddHandler again.
func RemoveHandlerLevels(h Handler, levels ...Level) {
func removeHandlerLevels(h Handler, levels ...Level) {
rw.Lock()
defer rw.Unlock()
OUTER:
Expand Down
8 changes: 4 additions & 4 deletions log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1038,27 +1038,27 @@ func TestRemoveHandlerLevels(t *testing.T) {
}
logHandlers = map[Level][]Handler{}
AddHandler(th, InfoLevel)
RemoveHandlerLevels(th, InfoLevel)
removeHandlerLevels(th, InfoLevel)
if len(logHandlers) != 0 {
t.Error("expected 0 handlers")
}

AddHandler(th, InfoLevel)
AddHandler(th2, InfoLevel)
RemoveHandlerLevels(th, InfoLevel)
removeHandlerLevels(th, InfoLevel)
if len(logHandlers) != 1 {
t.Error("expected 1 handlers left")
}
if len(logHandlers[InfoLevel]) != 1 {
t.Error("expected 1 handlers with InfoLevel left")
}
RemoveHandlerLevels(th2, InfoLevel)
removeHandlerLevels(th2, InfoLevel)
if len(logHandlers) != 0 {
t.Error("expected 0 handlers")
}

AddHandler(th, AllLevels...)
RemoveHandlerLevels(th, DebugLevel)
removeHandlerLevels(th, DebugLevel)
if len(logHandlers) != 7 {
t.Error("expected 7 log levels left")
}
Expand Down

0 comments on commit d55926d

Please sign in to comment.