Skip to content

Commit

Permalink
Support writing to a named logger (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
unknwon authored Nov 22, 2020
1 parent 9c91e63 commit ff3132f
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 68 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
name: Test
strategy:
matrix:
go-version: [1.13.x, 1.14.x]
go-version: [1.14.x, 1.15.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
Expand Down
105 changes: 75 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,26 @@ The code inside `init` function is equivalent to the following:

```go
func init() {
err := log.NewConsole(0, log.ConsoleConfig{
Level: log.LevelTrace,
})
err := log.NewConsole(0,
log.ConsoleConfig{
Level: log.LevelTrace,
},
)
if err != nil {
panic("unable to create new logger: " + err.Error())
}
}
```

Or expand further:

```go
func init() {
err := log.NewConsoleWithName(log.DefaultConsoleName, 0,
log.ConsoleConfig{
Level: log.LevelTrace,
},
)
if err != nil {
panic("unable to create new logger: " + err.Error())
}
Expand All @@ -70,9 +87,11 @@ In production, you may want to make log less verbose and be asynchronous:
func init() {
// The buffer size mainly depends on number of logs could be produced at the same time,
// 100 is a good default.
err := log.NewConsole(100, log.ConsoleConfig{
Level: log.LevelInfo,
})
err := log.NewConsole(100,
log.ConsoleConfig{
Level: log.LevelInfo,
},
)
if err != nil {
panic("unable to create new logger: " + err.Error())
}
Expand All @@ -94,10 +113,12 @@ func init() {
if err != nil {
panic("unable to create new logger: " + err.Error())
}
err := log.NewFile(log.FileConfig{
Level: log.LevelInfo,
Filename: "clog.log",
})
err := log.NewFile(
log.FileConfig{
Level: log.LevelInfo,
Filename: "clog.log",
},
)
if err != nil {
panic("unable to create new logger: " + err.Error())
}
Expand All @@ -106,6 +127,22 @@ func init() {

In this example, all logs will be printed to console, and only logs with level Info or higher (i.e. Warn, Error and Fatal) will be written into file.

### Write to a specific logger

When multiple loggers are registered, it is also possible to write logs to a special logger by giving its name.

```go
func main() {
log.TraceTo(log.DefaultConsoleName, "Hello %s!", "World")
log.InfoTo(log.DefaultConsoleName, "Hello %s!", "World")
log.WarnTo(log.DefaultConsoleName, "Hello %s!", "World")
log.ErrorTo(log.DefaultConsoleName, "So bad... %v", err)
log.FatalTo(log.DefaultConsoleName, "Boom! %v", err)

// ...
}
```

### Caller Location

When using `log.Error` and `log.Fatal` functions, the caller location is written along with logs.
Expand Down Expand Up @@ -134,14 +171,16 @@ File logger is the single most powerful builtin logger, it has the ability to ro

```go
func init() {
err := log.NewFile(100, log.FileConfig{
Level: log.LevelInfo,
Filename: "clog.log",
FileRotationConfig: log.FileRotationConfig {
Rotate: true,
Daily: true,
},
})
err := log.NewFile(100,
log.FileConfig{
Level: log.LevelInfo,
Filename: "clog.log",
FileRotationConfig: log.FileRotationConfig {
Rotate: true,
Daily: true,
},
},
)
if err != nil {
panic("unable to create new logger: " + err.Error())
}
Expand All @@ -152,10 +191,12 @@ In case you have some other packages that write to a file, and you want to take

```go
func init() {
w, err := log.NewFileWriter("filename", log.FileRotationConfig{
Rotate: true,
Daily: true,
})
w, err := log.NewFileWriter("filename",
log.FileRotationConfig{
Rotate: true,
Daily: true,
},
)
if err != nil {
panic("unable to create new logger: " + err.Error())
}
Expand All @@ -168,10 +209,12 @@ Slack logger is also supported in a simple way:

```go
func init() {
err := log.NewSlack(100, log.SlackConfig{
Level: log.LevelInfo,
URL: "https://url-to-slack-webhook",
})
err := log.NewSlack(100,
log.SlackConfig{
Level: log.LevelInfo,
URL: "https://url-to-slack-webhook",
},
)
if err != nil {
panic("unable to create new logger: " + err.Error())
}
Expand All @@ -186,10 +229,12 @@ Discord logger is supported in rich format via [Embed Object](https://discordapp

```go
func init() {
err := log.NewDiscord(100, log.DiscordConfig{
Level: log.LevelInfo,
URL: "https://url-to-discord-webhook",
})
err := log.NewDiscord(100,
log.DiscordConfig{
Level: log.LevelInfo,
URL: "https://url-to-discord-webhook",
},
)
if err != nil {
panic("unable to create new logger: " + err.Error())
}
Expand Down
50 changes: 46 additions & 4 deletions clog.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,7 @@ func Fatal(format string, v ...interface{}) {
// In test environment, Fatal or FatalDepth won't stop the manager or exit the program.
var isTestEnv = false

// FatalDepth writes formatted log with given skip depth in Fatal level then exits.
func FatalDepth(skip int, format string, v ...interface{}) {
mgr.write(LevelFatal, skip, format, v...)

func exit() {
if isTestEnv {
return
}
Expand All @@ -82,6 +79,51 @@ func FatalDepth(skip int, format string, v ...interface{}) {
os.Exit(1)
}

// FatalDepth writes formatted log with given skip depth in Fatal level then exits.
func FatalDepth(skip int, format string, v ...interface{}) {
mgr.write(LevelFatal, skip, format, v...)
exit()
}

// TraceTo writes formatted log in Trace level to the logger with given name.
func TraceTo(name, format string, v ...interface{}) {
mgr.writeTo(name, LevelTrace, 0, format, v...)
}

// InfoTo writes formatted log in Info level to the logger with given name.
func InfoTo(name, format string, v ...interface{}) {
mgr.writeTo(name, LevelInfo, 0, format, v...)
}

// WarnTo writes formatted log in Warn level to the logger with given name.
func WarnTo(name, format string, v ...interface{}) {
mgr.writeTo(name, LevelWarn, 0, format, v...)
}

// ErrorTo writes formatted log in Error level to the logger with given name.
func ErrorTo(name, format string, v ...interface{}) {
ErrorDepthTo(name, 4, format, v...)
}

// ErrorDepthTo writes formatted log with given skip depth in Error level to
// the logger with given name.
func ErrorDepthTo(name string, skip int, format string, v ...interface{}) {
mgr.writeTo(name, LevelError, skip, format, v...)
}

// FatalTo writes formatted log in Fatal level to the logger with given name
// then exits.
func FatalTo(name, format string, v ...interface{}) {
FatalDepthTo(name, 4, format, v...)
}

// FatalDepthTo writes formatted log with given skip depth in Fatal level to
// the logger with given name then exits.
func FatalDepthTo(name string, skip int, format string, v ...interface{}) {
mgr.writeTo(name, LevelFatal, skip, format, v...)
exit()
}

// Stop propagates cancellation to all loggers and waits for completion.
// This function should always be called before exiting the program.
func Stop() {
Expand Down
115 changes: 93 additions & 22 deletions clog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,36 +36,36 @@ func (l *chanLogger) Write(m Messager) error {
return nil
}

func Test_chanLogger(t *testing.T) {
initer := func(name string, level Level) Initer {
return func(_ string, vs ...interface{}) (Logger, error) {
var cfg *chanConfig
for i := range vs {
switch v := vs[i].(type) {
case chanConfig:
cfg = &v
}
}

if cfg == nil {
return nil, fmt.Errorf("config object with the type '%T' not found", chanConfig{})
func chanLoggerIniter(name string, level Level) Initer {
return func(_ string, vs ...interface{}) (Logger, error) {
var cfg *chanConfig
for i := range vs {
switch v := vs[i].(type) {
case chanConfig:
cfg = &v
}
}

return &chanLogger{
c: cfg.c,
noopLogger: &noopLogger{
name: name,
level: level,
},
}, nil
if cfg == nil {
return nil, fmt.Errorf("config object with the type '%T' not found", chanConfig{})
}

return &chanLogger{
c: cfg.c,
noopLogger: &noopLogger{
name: name,
level: level,
},
}, nil
}
}

func Test_chanLogger(t *testing.T) {
test1 := "mode1"
test1Initer := initer(test1, LevelTrace)
test1Initer := chanLoggerIniter(test1, LevelTrace)

test2 := "mode2"
test2Initer := initer(test2, LevelError)
test2Initer := chanLoggerIniter(test2, LevelError)

c1 := make(chan string)
c2 := make(chan string)
Expand Down Expand Up @@ -130,3 +130,74 @@ func Test_chanLogger(t *testing.T) {
})
}
}

func Test_writeToNamedLogger(t *testing.T) {
test1 := "alice"
test1Initer := chanLoggerIniter(test1, LevelTrace)

test2 := "bob"
test2Initer := chanLoggerIniter(test2, LevelTrace)

c1 := make(chan string)
c2 := make(chan string)

defer Remove(test1)
defer Remove(test2)
assert.Nil(t, New(test1, test1Initer, 1, chanConfig{
c: c1,
}))
assert.Nil(t, New(test2, test2Initer, 1, chanConfig{
c: c2,
}))

tests := []struct {
name string
fn func(string, string, ...interface{})
containsStr1 string
containsStr2 string
}{
{
name: "trace",
fn: TraceTo,
containsStr1: "[TRACE] log message",
containsStr2: "",
},
{
name: "info",
fn: InfoTo,
containsStr1: "[ INFO] log message",
containsStr2: "",
},
{
name: "warn",
fn: WarnTo,
containsStr1: "[ WARN] log message",
containsStr2: "",
},
{
name: "error",
fn: ErrorTo,
containsStr1: "()] log message",
containsStr2: "",
},
{
name: "fatal",
fn: FatalTo,
containsStr1: "()] log message",
containsStr2: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, 2, mgr.len())

tt.fn(test1, "log message")

assert.Contains(t, <-c1, tt.containsStr1)

if tt.containsStr2 != "" {
assert.Contains(t, <-c2, tt.containsStr2)
}
})
}
}
Loading

0 comments on commit ff3132f

Please sign in to comment.