Skip to content

Commit 953f720

Browse files
committed
README: update docs
[SKIP CI]
1 parent a6b3750 commit 953f720

File tree

1 file changed

+138
-67
lines changed

1 file changed

+138
-67
lines changed

README.md

+138-67
Original file line numberDiff line numberDiff line change
@@ -10,150 +10,221 @@ This package supports multiple loggers across different levels of logging. It us
1010

1111
## Installation
1212

13-
To use a tagged revision:
13+
The minimum requirement of Go is **1.11**.
1414

1515
go get unknwon.dev/clog/v2
1616

1717
Please apply `-u` flag to update in the future.
1818

19-
### Testing
20-
21-
If you want to test on your machine, please apply `-t` flag:
22-
23-
go get -t unknwon.dev/clog/v2
24-
25-
Please apply `-u` flag to update in the future.
26-
2719
## Getting Started
2820

29-
Clog currently has four builtin logger adapters: `console`, `file`, `slack` and `discord`.
30-
3121
It is extremely easy to create one with all default settings. Generally, you would want to create new logger inside `init` or `main` function.
3222

33-
```go
34-
...
23+
Let's create a logger that prints logs to the console:
3524

25+
```go
3626
import (
37-
"fmt"
38-
"os"
39-
4027
log "unknwon.dev/clog/v2"
4128
)
4229

4330
func init() {
44-
// 0 means logging synchronously
45-
err := log.New(log.ModeConsole, 0, log.ConsoleConfig{})
31+
err := log.New(log.ModeConsole)
4632
if err != nil {
47-
fmt.Printf("Fail to create new logger: %v\n", err)
48-
os.Exit(1)
33+
panic("unable to create new logger: " + err.Error())
4934
}
35+
}
5036

51-
log.Trace("Hello %s!", "Clog")
52-
// Output: Hello Clog!
37+
func main() {
38+
log.Trace("Hello %s!", "World") // YYYY/MM/DD 12:34:56 [TRACE] Hello World!
39+
log.Info("Hello %s!", "World") // YYYY/MM/DD 12:34:56 [ INFO] Hello World!
40+
log.Warn("Hello %s!", "World") // YYYY/MM/DD 12:34:56 [ WARN] Hello World!
5341

54-
log.Info("Hello %s!", "Clog")
55-
log.Warn("Hello %s!", "Clog")
56-
...
57-
5842
// Graceful stopping all loggers before exiting the program.
5943
log.Stop()
6044
}
61-
62-
...
6345
```
6446

65-
The above code is equivalent to the follow settings:
47+
The code inside `init` function is equivalent to the following:
6648

6749
```go
68-
...
50+
func init() {
6951
err := log.New(log.ModeConsole, 0, log.ConsoleConfig{
70-
Level: log.LevelTrace, // Record all logs
52+
Level: log.LevelTrace,
7153
})
72-
...
54+
if err != nil {
55+
panic("unable to create new logger: " + err.Error())
56+
}
57+
}
7358
```
7459

75-
In production, you may want to make log less verbose and asynchronous:
60+
- The `0` is an integer type so it is used as underlying buffer size. In this case, `0` creates synchronized logger (call hangs until write is finished).
61+
- Any non-integer type is used as the config object, in this case `ConsoleConfig` is the respective config object for the console logger.
62+
- The `LevelTrace` used here is the lowest logging level, meaning prints every log to the console. All levels from lowest to highest are: `LevelTrace`, `LevelInfo`, `LevelWarn`, `LevelError`, `LevelFatal`, each of has at least one repective function, e.g. `log.Trace`, `log.Info`, `log.Warn`, `log.Error` and `log.Fatal`.
63+
64+
In production, you may want to make log less verbose and be asynchronous:
7665

7766
```go
78-
...
79-
// The buffer size mainly depends on how many logs will be produced at the same time,
80-
// 100 is a good default.
67+
func init() {
68+
// The buffer size mainly depends on how many logs will be produced at the same time, 100 is a good default.
8169
err := log.New(log.ModeConsole, 100, log.ConsoleConfig{
82-
// Logs under Info level (in this case Trace) will be discarded.
8370
Level: log.LevelInfo,
8471
})
85-
...
72+
if err != nil {
73+
panic("unable to create new logger: " + err.Error())
74+
}
75+
}
8676
```
8777

88-
Console logger comes with color output, but for non-colorable destination, the color output will be disabled automatically.
78+
- When you set level to be `LevelInfo`, calls to the `log.Trace` will be simply noop.
79+
- The console logger comes with color output, but for non-colorable destination, the color output will be disabled automatically.
80+
81+
Other builtin loggers are file (`log.ModeFile`), Slack (`log.ModeSlack`) and Discord (`log.Discord`), see later in the documentation for usage details.
8982

90-
### Error Location
83+
### Caller Location
9184

92-
When using `log.Error` and `log.Fatal` functions, the caller location is printed along with the message.
85+
When using `log.Error` and `log.Fatal` functions, the caller location is written along with logs.
9386

9487
```go
95-
...
96-
log.Error("So bad... %v", err)
97-
// Output: 2017/02/09 01:06:16 [ERROR] [...uban-builder/main.go:64 main()] ...
98-
log.Fatal("Boom! %v", err)
99-
// Output: 2017/02/09 01:06:16 [FATAL] [...uban-builder/main.go:64 main()] ...
100-
...
101-
```
88+
func main() {
89+
log.Error("So bad... %v", err) // YYYY/MM/DD 12:34:56 [ERROR] [...er/main.go:64 main()] ...
90+
log.Fatal("Boom! %v", err) // YYYY/MM/DD 12:34:56 [FATAL] [...er/main.go:64 main()] ...
10291

103-
Calling `log.Fatal` will exit the program.
92+
// ...
93+
}
94+
```
10495

105-
If you want to have different skip depth than the default, you can use `log.ErrorDepth` or `log.FatalDepth`.
96+
- Calling `log.Fatal` will exit the program.
97+
- If you want to have different skip depth than the default, use `log.ErrorDepth` or `log.FatalDepth`.
10698

10799
### Clean Exit
108100

109-
You should always call `log.Stop()` to wait until all messages are processed before program exits.
101+
You should always call `log.Stop()` to wait until all logs are processed before program exits.
102+
103+
## Builtin Loggers
110104

111-
## File
105+
### File Logger
112106

113-
File logger is more complex than console, and it has ability to rotate:
107+
File logger is the single most powerful builtin logger, it has the ability to rotate based on file size, line, and date:
114108

115109
```go
116-
...
110+
func init() {
117111
err := log.New(log.ModeFile, 100, log.FileConfig{
118-
Level: log.LevelInfo,
112+
Level: log.LevelInfo,
119113
Filename: "clog.log",
120114
FileRotationConfig: log.FileRotationConfig {
121115
Rotate: true,
122116
Daily: true,
123117
},
124118
})
125-
...
119+
if err != nil {
120+
panic("unable to create new logger: " + err.Error())
121+
}
122+
}
123+
```
124+
125+
In case you have some other packages that write to a file, and you want to take advatange of this file rotation feature. You can do so by using the `log.NewFileWriter` function. It acts like a standard `io.Writer`.
126+
127+
```go
128+
func init() {
129+
w, err := log.NewFileWriter("filename", log.FileRotationConfig{
130+
Rotate: true,
131+
Daily: true,
132+
})
133+
if err != nil {
134+
panic("unable to create new logger: " + err.Error())
135+
}
136+
}
126137
```
127138

128-
## Slack
139+
### Slack Logger
129140

130141
Slack logger is also supported in a simple way:
131142

132143
```go
133-
...
144+
func init() {
134145
err := log.New(log.ModeSlack, 100, log.SlackConfig{
135-
Level: log.LevelInfo,
136-
URL: "https://url-to-slack-webhook",
146+
Level: log.LevelInfo,
147+
URL: "https://url-to-slack-webhook",
137148
})
138-
...
149+
if err != nil {
150+
panic("unable to create new logger: " + err.Error())
151+
}
152+
}
139153
```
140154

141155
This logger also works for [Discord Slack](https://discordapp.com/developers/docs/resources/webhook#execute-slackcompatible-webhook) endpoint.
142156

143-
## Discord
157+
### Discord Logger
144158

145159
Discord logger is supported in rich format via [Embed Object](https://discordapp.com/developers/docs/resources/channel#embed-object):
146160

147161
```go
148-
...
162+
func init() {
149163
err := log.New(log.ModeDiscord, 100, log.DiscordConfig{
150-
Level: log.LevelInfo,
151-
URL: "https://url-to-discord-webhook",
164+
Level: log.LevelInfo,
165+
URL: "https://url-to-discord-webhook",
152166
})
153-
...
167+
if err != nil {
168+
panic("unable to create new logger: " + err.Error())
169+
}
170+
}
171+
```
172+
173+
This logger automatically retries up to 3 times if hits rate limit with respect to `retry_after`.
174+
175+
## Build Your Own Logger
176+
177+
You can implement your own logger and all the concurrency stuff are handled automatically!
178+
179+
Here is an example which sends all logs to a channel, we call it `chanLogger` here:
180+
181+
```go
182+
import log "unknwon.dev/clog/v2"
183+
184+
const modeChannel log.Mode = "channel"
185+
186+
type chanConfig struct {
187+
c chan string
188+
}
189+
190+
var _ log.Logger = (*chanLogger)(nil)
191+
192+
type chanLogger struct {
193+
level log.Level
194+
c chan string
195+
}
196+
197+
func (*chanLogger) Mode() log.Mode { return modeChannel }
198+
func (l *chanLogger) Level() log.Level { return l.level }
199+
200+
func (l *chanLogger) Write(m Messager) error {
201+
l.c <- m.String()
202+
return nil
203+
}
204+
205+
func init() {
206+
log.NewRegister(modeChannel, func(v interface{}) (log.Logger, error) {
207+
if v == nil {
208+
v = chanConfig{}
209+
}
210+
211+
cfg, ok := v.(chanConfig)
212+
if !ok {
213+
return nil, fmt.Errorf("invalid config object: want %T got %T", chanConfig{}, v)
214+
}
215+
216+
if cfg.c == nil {
217+
return nil, errors.New("channel is nil")
218+
}
219+
220+
return &chanLogger{
221+
c: cfg.c,
222+
}, nil
223+
})
224+
}
154225
```
155226

156-
This logger also retries automatically if hits rate limit after `retry_after`.
227+
Have fun!
157228

158229
## Credits
159230

0 commit comments

Comments
 (0)