Skip to content

Commit b8df3d2

Browse files
committed
improved design, non-blocking serial access logging
1 parent cead8d0 commit b8df3d2

14 files changed

+1202
-989
lines changed

console_receiver.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright (c) Jeevanandam M (https://github.com/jeevatkm)
2+
// go-aah/log source code and usage is governed by a MIT style
3+
// license that can be found in the LICENSE file.
4+
5+
package log
6+
7+
import (
8+
"fmt"
9+
"io"
10+
"os"
11+
"runtime"
12+
13+
"aahframework.org/config.v0"
14+
)
15+
16+
var (
17+
// ANSI color codes
18+
resetColor = []byte("\033[0m")
19+
levelToColor = [][]byte{
20+
levelFatal: []byte("\033[0;31m"), // red
21+
levelPanic: []byte("\033[0;31m"), // red
22+
LevelError: []byte("\033[0;31m"), // red
23+
LevelWarn: []byte("\033[0;33m"), // yellow
24+
LevelInfo: []byte("\033[0;37m"), // white
25+
LevelDebug: []byte("\033[0;34m"), // blue
26+
LevelTrace: []byte("\033[0;35m"), // magenta (purple)
27+
}
28+
29+
_ Receiver = &ConsoleReceiver{}
30+
)
31+
32+
// ConsoleReceiver writes the log entry into os.Stderr.
33+
// For non-windows it writes with color.
34+
type ConsoleReceiver struct {
35+
out io.Writer
36+
formatter string
37+
flags *[]FlagPart
38+
isCallerInfo bool
39+
isColor bool
40+
}
41+
42+
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
43+
// ConsoleReceiver methods
44+
//___________________________________
45+
46+
// Init method initializes the console logger.
47+
func (c *ConsoleReceiver) Init(cfg *config.Config) error {
48+
c.out = os.Stderr
49+
c.isColor = runtime.GOOS != "windows"
50+
51+
c.formatter = cfg.StringDefault("log.format", "text")
52+
if !(c.formatter == textFmt || c.formatter == jsonFmt) {
53+
return fmt.Errorf("log: unsupported format '%s'", c.formatter)
54+
}
55+
56+
return nil
57+
}
58+
59+
// SetPattern method initializes the logger format pattern.
60+
func (c *ConsoleReceiver) SetPattern(pattern string) error {
61+
flags, err := parseFlag(pattern)
62+
if err != nil {
63+
return err
64+
}
65+
c.flags = flags
66+
if c.formatter == textFmt {
67+
c.isCallerInfo = isCallerInfo(c.flags)
68+
}
69+
return nil
70+
}
71+
72+
// IsCallerInfo method returns true if log receiver is configured with caller info
73+
// otherwise false.
74+
func (c *ConsoleReceiver) IsCallerInfo() bool {
75+
return c.isCallerInfo
76+
}
77+
78+
// Log method writes the log entry into os.Stderr.
79+
func (c *ConsoleReceiver) Log(entry *Entry) {
80+
if c.isColor {
81+
_, _ = c.out.Write(levelToColor[entry.Level])
82+
}
83+
84+
msg := applyFormatter(c.formatter, c.flags, entry)
85+
if len(msg) == 0 || msg[len(msg)-1] != '\n' {
86+
msg = append(msg, '\n')
87+
}
88+
_, _ = c.out.Write(msg)
89+
90+
if c.isColor {
91+
_, _ = c.out.Write(resetColor)
92+
}
93+
}

console_receiver_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright (c) Jeevanandam M (https://github.com/jeevatkm)
2+
// go-aah/log source code and usage is governed by a MIT style
3+
// license that can be found in the LICENSE file.
4+
5+
package log
6+
7+
import (
8+
"testing"
9+
"time"
10+
11+
"aahframework.org/config.v0"
12+
"aahframework.org/test.v0/assert"
13+
)
14+
15+
func TestConsoleLoggerTextJSON(t *testing.T) {
16+
// Text 1
17+
textConfigStr1 := `
18+
log {
19+
receiver = "console"
20+
level = "debug"
21+
pattern = "%utctime:2006-01-02 15:04:05.000 %level:-5 %longfile %line %custom:- %message"
22+
}
23+
`
24+
testConsoleLogger(t, textConfigStr1)
25+
26+
// Text 2
27+
textConfigStr2 := `
28+
log {
29+
receiver = "console"
30+
level = "debug"
31+
pattern = "%time:2006-01-02 15:04:05.000 %level:-5 %shortfile %line %custom:- %message"
32+
}
33+
`
34+
testConsoleLogger(t, textConfigStr2)
35+
36+
// JSON
37+
jsonConfigStr := `
38+
log {
39+
receiver = "console"
40+
level = "debug"
41+
format = "json"
42+
}
43+
`
44+
testConsoleLogger(t, jsonConfigStr)
45+
}
46+
47+
func TestConsoleLoggerUnsupportedFormat(t *testing.T) {
48+
configStr := `
49+
log {
50+
# default config plus
51+
pattern = "%utctime:2006-01-02 15:04:05.000 %level:-5 %longfile %line %custom:- %message"
52+
format = "xml"
53+
}
54+
`
55+
cfg, _ := config.ParseString(configStr)
56+
logger, err := New(cfg)
57+
assert.Nil(t, logger)
58+
assert.Equal(t, "log: unsupported format 'xml'", err.Error())
59+
}
60+
61+
func TestConsoleLoggerUnknownFormatFlag(t *testing.T) {
62+
configStr := `
63+
log {
64+
# default config plus
65+
pattern = "%time:2006-01-02 15:04:05.000 %level:-5 %myfile %line %custom:- %message"
66+
}
67+
`
68+
cfg, _ := config.ParseString(configStr)
69+
logger, err := New(cfg)
70+
assert.Nil(t, logger)
71+
assert.Equal(t, "unrecognized log format flag: myfile", err.Error())
72+
}
73+
74+
func TestConsoleLoggerDefaults(t *testing.T) {
75+
configStr := `
76+
log {
77+
# default config
78+
}
79+
`
80+
cfg, _ := config.ParseString(configStr)
81+
logger, err := New(cfg)
82+
assert.NotNil(t, logger)
83+
assert.Nil(t, err)
84+
}
85+
86+
func testConsoleLogger(t *testing.T, cfgStr string) {
87+
cfg, _ := config.ParseString(cfgStr)
88+
logger, err := New(cfg)
89+
assert.FailNowOnError(t, err, "unexpected error")
90+
91+
logger.Trace("I shoudn't see this msg, because standard logger level is DEBUG")
92+
logger.Tracef("I shoudn't see this msg, because standard logger level is DEBUG: %v", 4)
93+
94+
logger.Debug("I would like to see this message, debug is useful for dev")
95+
logger.Debugf("I would like to see this message, debug is useful for %v", "dev")
96+
97+
logger.Info("Yes, I would love to see")
98+
logger.Infof("Yes, I would love to %v", "see")
99+
100+
logger.Warn("Yes, yes it's an warning")
101+
logger.Warnf("Yes, yes it's an %v", "warning")
102+
103+
logger.Error("Yes, yes, yes - finally an error")
104+
logger.Errorf("Yes, yes, yes - %v", "finally an error")
105+
106+
time.Sleep(1 * time.Millisecond)
107+
}

default.go

Lines changed: 76 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,106 +7,123 @@ package log
77
import (
88
"fmt"
99
"os"
10-
)
1110

12-
var stdLogger Logger
11+
"aahframework.org/config.v0"
12+
)
1313

14-
// Fatal logs message as `FATAL` and calls os.Exit(1)
15-
func Fatal(v ...interface{}) {
16-
_ = stdLogger.Output(levelFatal, 2, nil, v...)
17-
os.Exit(1)
18-
}
14+
var std *Logger
1915

20-
// Fatalf logs message as `FATAL` and calls os.Exit(1)
21-
func Fatalf(format string, v ...interface{}) {
22-
_ = stdLogger.Output(levelFatal, 2, &format, v...)
23-
os.Exit(1)
24-
}
16+
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
17+
// Logger methods
18+
//_______________________________________
2519

26-
// Panic logs message as `PANIC` and calls panic()
27-
func Panic(v ...interface{}) {
28-
_ = stdLogger.Output(levelPanic, 2, nil, v...)
29-
panic("")
30-
}
31-
32-
// Panicf logs message as `PANIC` and calls panic()
33-
func Panicf(format string, v ...interface{}) {
34-
_ = stdLogger.Output(levelPanic, 2, &format, v...)
35-
panic(fmt.Sprintf(format, v...))
36-
}
37-
38-
// Error logs message as `LevelError`
20+
// Error logs message as `ERROR`. Arguments handled in the mananer of `fmt.Print`.
3921
func Error(v ...interface{}) {
40-
_ = stdLogger.Output(LevelError, 2, nil, v...)
22+
std.output(LevelError, 3, nil, v...)
4123
}
4224

43-
// Errorf logs message as `LevelError`
25+
// Errorf logs message as `ERROR`. Arguments handled in the mananer of `fmt.Printf`.
4426
func Errorf(format string, v ...interface{}) {
45-
_ = stdLogger.Output(LevelError, 2, &format, v...)
27+
std.output(LevelError, 3, &format, v...)
4628
}
4729

48-
// Warn logs message as `LevelWarn`
30+
// Warn logs message as `WARN`. Arguments handled in the mananer of `fmt.Print`.
4931
func Warn(v ...interface{}) {
50-
_ = stdLogger.Output(LevelWarn, 2, nil, v...)
32+
std.output(LevelWarn, 3, nil, v...)
5133
}
5234

53-
// Warnf logs message as `LevelWarn`
35+
// Warnf logs message as `WARN`. Arguments handled in the mananer of `fmt.Printf`.
5436
func Warnf(format string, v ...interface{}) {
55-
_ = stdLogger.Output(LevelWarn, 2, &format, v...)
37+
std.output(LevelWarn, 3, &format, v...)
5638
}
5739

58-
// Info logs message as `LevelInfo`
40+
// Info logs message as `INFO`. Arguments handled in the mananer of `fmt.Print`.
5941
func Info(v ...interface{}) {
60-
_ = stdLogger.Output(LevelInfo, 2, nil, v...)
42+
std.output(LevelInfo, 3, nil, v...)
6143
}
6244

63-
// Infof logs message as `LevelInfo`
45+
// Infof logs message as `INFO`. Arguments handled in the mananer of `fmt.Printf`.
6446
func Infof(format string, v ...interface{}) {
65-
_ = stdLogger.Output(LevelInfo, 2, &format, v...)
47+
std.output(LevelInfo, 3, &format, v...)
6648
}
6749

68-
// Debug logs message as `LevelDebug`
50+
// Debug logs message as `DEBUG`. Arguments handled in the mananer of `fmt.Print`.
6951
func Debug(v ...interface{}) {
70-
_ = stdLogger.Output(LevelDebug, 2, nil, v...)
52+
std.output(LevelDebug, 3, nil, v...)
7153
}
7254

73-
// Debugf logs message as `LevelDebug`
55+
// Debugf logs message as `DEBUG`. Arguments handled in the mananer of `fmt.Printf`.
7456
func Debugf(format string, v ...interface{}) {
75-
_ = stdLogger.Output(LevelDebug, 2, &format, v...)
57+
std.output(LevelDebug, 3, &format, v...)
7658
}
7759

78-
// Trace logs message as `LevelTrace`
60+
// Trace logs message as `TRACE`. Arguments handled in the mananer of `fmt.Print`.
7961
func Trace(v ...interface{}) {
80-
_ = stdLogger.Output(LevelTrace, 2, nil, v...)
62+
std.output(LevelTrace, 3, nil, v...)
8163
}
8264

83-
// Tracef logs message as `LevelTrace`
65+
// Tracef logs message as `TRACE`. Arguments handled in the mananer of `fmt.Printf`.
8466
func Tracef(format string, v ...interface{}) {
85-
_ = stdLogger.Output(LevelTrace, 2, &format, v...)
67+
std.output(LevelTrace, 3, &format, v...)
8668
}
8769

88-
// Stats returns current logger statistics like number of lines written,
89-
// number of bytes written, etc.
90-
func Stats() *ReceiverStats {
91-
return stdLogger.Stats()
70+
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
71+
// Logger methods - Drop-in replacement
72+
// for Go standard logger
73+
//_______________________________________
74+
75+
// Print logs message as `INFO`. Arguments handled in the mananer of `fmt.Print`.
76+
func Print(v ...interface{}) {
77+
std.output(LevelInfo, 3, nil, v...)
9278
}
9379

94-
// SetPattern sets the log entry format
95-
func SetPattern(pattern string) error {
96-
return stdLogger.SetPattern(pattern)
80+
// Printf logs message as `INFO`. Arguments handled in the mananer of `fmt.Printf`.
81+
func Printf(format string, v ...interface{}) {
82+
std.output(LevelInfo, 3, &format, v...)
9783
}
9884

99-
// SetLevel allows to set log level dynamically
100-
func SetLevel(level Level) {
101-
stdLogger.SetLevel(level)
85+
// Println logs message as `INFO`. Arguments handled in the mananer of `fmt.Printf`.
86+
func Println(format string, v ...interface{}) {
87+
std.output(LevelInfo, 3, &format, v...)
10288
}
10389

104-
// SetOutput allows to set standard logger implementation
105-
// which statisfies `Logger` interface
106-
func SetOutput(logger Logger) {
107-
stdLogger = logger
90+
// Fatal logs message as `FATAL` and call to os.Exit(1).
91+
func Fatal(v ...interface{}) {
92+
std.output(levelFatal, 3, nil, v...)
93+
os.Exit(1)
94+
}
95+
96+
// Fatalf logs message as `FATAL` and call to os.Exit(1).
97+
func Fatalf(format string, v ...interface{}) {
98+
std.output(levelFatal, 3, &format, v...)
99+
os.Exit(1)
100+
}
101+
102+
// Fatalln logs message as `FATAL` and call to os.Exit(1).
103+
func Fatalln(format string, v ...interface{}) {
104+
std.output(levelFatal, 3, &format, v...)
105+
os.Exit(1)
106+
}
107+
108+
// Panic logs message as `PANIC` and call to panic().
109+
func Panic(v ...interface{}) {
110+
std.output(levelPanic, 3, nil, v...)
111+
panic("")
112+
}
113+
114+
// Panicf logs message as `PANIC` and call to panic().
115+
func Panicf(format string, v ...interface{}) {
116+
std.output(levelPanic, 3, &format, v...)
117+
panic(fmt.Sprintf(format, v...))
118+
}
119+
120+
// Panicln logs message as `PANIC` and call to panic().
121+
func Panicln(format string, v ...interface{}) {
122+
std.output(levelPanic, 3, &format, v...)
123+
panic(fmt.Sprintf(format, v...))
108124
}
109125

110126
func init() {
111-
stdLogger, _ = New(`receiver = "CONSOLE"; level = "DEBUG";`)
127+
cfg, _ := config.ParseString("log { }")
128+
std, _ = New(cfg)
112129
}

0 commit comments

Comments
 (0)