[中文版本]

RainbowLog is an easy-to-use, highly configurable, structured logging library (For Golang).
It is designed to provide fast and reliable logging services for other projects of Rainbow Project
and is one of the foundations of Rainbow Project.

When designing RainbowLog, we referred to some of the design ideas of ZeroLog,
and combined it with some of the features needed in my work to make RainbowLog faster , more efficient, Smaller,
more beautiful and easier to use.

How to use

There are instructions for use (in English) in README.md.

Note: README.md may not fully describe all the functions of Rainbow Log.
I will add the missing parts to README at the appropriate time.

Installation

1
go get -u github.com/rambollwong/rainbowlog

Use global logger

Use global logger by default options

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"github.com/rambollwong/rainbowlog/log"
)

func main() {
log.UseDefault()

log.Logger.Info().Msg("Hello world!").Done()
}

// Output: {"_TIME_":"2024-02-19 19:50:09.008","_LEVEL_":"INFO","_CALLER_":"/path/to/main.go:10","message":"Hello world!"}

Note: By default log writes to os.Stderr

Use global logger by rainbow default options

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"errors"

"github.com/rambollwong/rainbowlog/log"
)

func main() {
log.UseRainbowDefault()

log.Logger.Info().Msg("Hello world!").Done()
log.Logger.Debug().WithLabels("MODEL1").Msg("Something debugging...").Done()
log.Logger.Warn().WithLabels("MODEL2", "SERVICE1").Msg("Something warning!").Int("IntegerValue", 888).Done()
log.Logger.Error().Msg("failed to do something").Err(errors.New("something wrong")).Done()
log.Logger.Fatal().Msg("fatal to do something").Done()
}

Output:

Use global logger by config file

RainbowLog supports setting logger options based on configuration files.
If you want to use a configuration file, you need to ensure that the configuration file contains Rainbow Log configuration items.
Rainbow Log supports configuration files in three formats: .yaml|.json|.toml. For specific configuration templates, see the corresponding files in the config package.

Assume that we have prepared a configuration file rainbowlog.yaml and placed it in the same directory as the execution file, then:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"errors"

"github.com/rambollwong/rainbowlog/log"
)

func main() {
log.UseDefaultConfigFile()

log.Logger.Info().Msg("Hello world!").Done()
log.Logger.Debug().WithLabels("MODEL1").Msg("Something debugging...").Done()
log.Logger.Warn().WithLabels("MODEL2", "SERVICE1").Msg("Something warning!").Int("IntegerValue", 888).Done()
log.Logger.Error().Msg("failed to do something").Err(errors.New("something wrong")).Done()
log.Logger.Fatal().Msg("fatal to do something").Done()
}

If you want to use a .json or .toml type configuration file, you only need to modify the log.DefaultConfigFileName, for example:

1
log.DefaultConfigFileName = "rainbowlog.json"

Or

1
log.DefaultConfigFileName = "rainbowlog.toml"

If you also want to specify the directory where the configuration file is located, you only need to modify log.DefaultConfigFilePath:

1
log.DefaultConfigFilePath = "/path/of/config/files"

Note: Modifying log.DefaultConfigFileName and log.DefaultConfigFilePath needs to be executed before log.UseDefaultConfigFile(), otherwise it will not take effect.

Use global logger by custom options

If you want to use custom options for Global logger, we have reserved the log.UseCustomOptions(opts ...Option) API for implementation.
Supported Option is detailed in option.go.

Custom logger

If you don’t want to use Global logger, you can initialize a Logger instance through the New method.
The New method receives the Option parameters.
Supported Option is detailed in option.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"errors"
"path/filepath"

"github.com/rambollwong/rainbowlog"
)

func main() {
DefaultConfigFileName := "rainbowlog.yaml"
DefaultConfigFilePath := "/path/of/config/files"

logger := rainbowlog.New(
rainbowlog.WithDefault(),
rainbowlog.WithConfigFile(filepath.Join(DefaultConfigFilePath, DefaultConfigFileName)),
)

logger.Info().Msg("Hello world!").Done()
logger.Debug().WithLabels("MODEL1").Msg("Something debugging...").Done()
logger.Warn().WithLabels("MODEL2", "SERVICE1").Msg("Something warning!").Int("IntegerValue", 888).Done()
logger.Error().Msg("failed to do something").Err(errors.New("something wrong")).Done()
logger.Fatal().Msg("fatal to do something").Done()
}

SubLogger

SubLogger support allows you to create a logger instance that inherits from the parent logger and can reset some Option when needed.
For example, a scenario where a LABEL different from the parent Logger is used in a submodule.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
"os"

"github.com/rambollwong/rainbowlog"
"github.com/rambollwong/rainbowlog/level"
)

func main() {
logger := rainbowlog.New(
rainbowlog.WithDefault(),
rainbowlog.AppendsEncoderWriters(rainbowlog.JsonEnc, os.Stderr),
rainbowlog.WithCallerMarshalFunc(nil),
rainbowlog.WithLevel(level.Info),
rainbowlog.WithLabels("ROOT"),
)

logger.Debug().Msg("Hello world!").Done()
logger.Info().Msg("Hello world!").Done()

subLogger := logger.SubLogger(
rainbowlog.WithLevel(level.Debug),
rainbowlog.WithLabels("SUBMODULE"),
)

subLogger.Debug().Msg("Hello world!").Done()
subLogger.Info().Msg("Hello world!").Done()
}

// Output:
// {"_TIME_":"2024-02-21 11:28:02.150","_LEVEL_":"INFO","_LABEL_":"ROOT","message": "Hello world!"}
// {"_TIME_":"2024-02-21 11:28:02.150","_LEVEL_":"DEBUG","_LABEL_":"SUBMODULE","message":"Hello world!"}
// {"_TIME_":"2024-02-21 11:28:02.150","_LEVEL_":"INFO","_LABEL_":"SUBMODULE","message":"Hello world!"}

Modify time output format

The default time format of RainbowLog is 2006-01-02 15:04:05.000,
you can modify this format through WithTimeFormat(timeFormat string) Option,
the format can be a string that conforms to golang time formatting rules,
it can also be UNIX or UNIXMS or UNIXMICRO or UNIXNANO,
They respectively represent the return value of Unix() or UnixMilli() or UnixMicro() or UnixNano() which outputs time.Time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"os"

"github.com/rambollwong/rainbowlog"
)

func main() {
logger := rainbowlog.New(
rainbowlog.WithDefault(),
rainbowlog.AppendsEncoderWriters(rainbowlog.JsonEnc, os.Stderr),
rainbowlog.WithTimeFormat(rainbowlog.TimeFormatUnix),
)

logger.Info().Msg("Hello world!").Done()
}

// Output:{"_TIME_":1708346689,"_LEVEL_":"INFO","_CALLER_":"main.go:16","message":"Hello world!"}

Hooks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
"fmt"
"os"

"github.com/rambollwong/rainbowlog"
"github.com/rambollwong/rainbowlog/level"
)

func main() {
var hook rainbowlog.HookFunc = func(r rainbowlog.Record, level level.Level, message string) {
fmt.Printf("hook: %s, %s\n", level.String(), message)
}
logger := rainbowlog.New(
rainbowlog.WithDefault(),
rainbowlog.AppendsEncoderWriters(rainbowlog.JsonEnc, os.Stderr),
rainbowlog.WithCallerMarshalFunc(nil),
rainbowlog.AppendsHooks(hook),
rainbowlog.WithLevel(level.Info),
)

logger.Debug().Msg("Hello world!").Done()
logger.Info().Msg("Hello world!").Done()
}

// Output:
// hook: info, Hello world!
// {"_TIME_":"2024-02-21 11:42:17.592","_LEVEL_":"INFO","message":"Hello world!"}

Github repository

https://github.com/rambollwong/rainbowlog

If you think what I did is pretty good, I hope you can give it a ⭐. Your support is my biggest motivation to move forward.