Skip to content

Support writing to a named logger #21

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

Merged
merged 2 commits into from
Nov 22, 2020
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
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