README
¶
RainbowLog

Simple, configurable, structured Go logging library.
RainbowLog's API is designed to provide an excellent developer experience and outstanding performance. Its unique chained API allows RainbowLog to write JSON log events by avoiding allocations and reflection.
To keep the codebase and API simple, RainbowLog focuses only on efficient structured logging.
Pretty log output on the console can be achieved through the provided (but less efficient) log.ConsolePacker.

Features
- High Performance: Zero memory allocation, no reflection logging
- Structured Logging: Supports JSON and text format log output
- Flexible Configuration: Supports multiple configuration methods (code configuration, configuration files, etc.)
- Multi-output Support: Can output to multiple targets simultaneously (files, standard output, etc.)
- Log Level Control: Supports Debug, Info, Warn, Error, Fatal, Panic, Trace levels
- Modular Labels: Supports adding labels to different modules for easy log classification
- Hook Mechanism: Supports custom hook functions to handle log events
- Sub Logger: Supports creating sub loggers that inherit parent configuration
- Error Stack Tracing: Supports error stack information output
- Caller Information: Supports recording the file and line number where the log was generated
Installation
go get -u github.com/rambollwong/rainbowlog
Quick Start
Using Global Logger
Global Logger with Default Options
package main
import (
"github.com/rambollwong/rainbowlog/logger"
)
func main() {
logger.UseDefault()
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, logs are written to
os.Stderr
Global Logger with Rainbow Default Options
package main
import (
"errors"
"github.com/rambollwong/rainbowlog/logger"
)
func main() {
logger.UseRainbowDefault()
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()
}
Output:

Global Logger with Configuration File
RainbowLog supports setting logger options based on configuration files.
To use a configuration file, you need to ensure that the configuration file contains RainbowLog configuration items.
RainbowLog supports three formats of configuration files: .yaml|.json|.toml. For specific configuration templates, please refer to the corresponding files in the config package.
Assuming we have prepared a configuration file rainbowlog.yaml and placed it in the same directory as the executable file:
package main
import (
"errors"
"github.com/rambollwong/rainbowlog/logger"
)
func main() {
logger.UseDefaultConfigFile()
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()
}
To use .json or .toml type configuration files, just modify logger.DefaultConfigFileName, for example:
logger.DefaultConfigFileName = "rainbowlog.json"
Or
logger.DefaultConfigFileName = "rainbowlog.toml"
If you also want to specify the directory where the configuration file is located, just modify logger.DefaultConfigFilePath:
logger.DefaultConfigFilePath = "/path/of/config/files"
Note: Modifying
logger.DefaultConfigFileNameandlogger.DefaultConfigFilePathneeds to be executed beforelogger.UseDefaultConfigFile(), otherwise it will not take effect.
Global Logger with Custom Options
If you want to use custom options for the global logger, we provide the logger.UseCustomOptions(opts ...log.Option) API to achieve this.
Supported Option details can be found in option.go.
Custom Logger
If you don't want to use the Global Logger, you can initialize a Logger instance through the New method.
The New method accepts Option parameters.
Supported Option details can be found in option.go.
package main
import (
"errors"
"path/filepath"
"github.com/rambollwong/rainbowlog/log"
)
func main() {
DefaultConfigFileName := "rainbowlog.yaml"
DefaultConfigFilePath := "/path/of/config/files"
logger := log.New(
log.WithDefault(),
log.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 an instance that inherits from the parent logger and reset certain Option when needed.
For example, in a submodule scenario where a different LABEL is used than the parent Logger.
package main
import (
"os"
"github.com/rambollwong/rainbowlog/log"
"github.com/rambollwong/rainbowlog/level"
)
func main() {
logger := log.New(
log.WithDefault(),
log.AppendsEncoderWriters(log.JsonEnc, os.Stderr),
log.WithCallerMarshalFunc(nil),
log.WithLevel(level.Info),
log.WithLabels("ROOT"),
)
logger.Debug().Msg("Hello world!").Done()
logger.Info().Msg("Hello world!").Done()
subLogger := logger.SubLogger(
log.WithLevel(level.Debug),
log.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!"}
Modifying Time Output Format
RainbowLog's default time format is 2006-01-02 15:04:05.000,
you can modify this format through the WithTimeFormat(timeFormat string) option,
the format can be a string that conforms to golang time format rules,
or it can be UNIX or UNIXMS or UNIXMICRO or UNIXNANO,
which represent the return values of Unix() or UnixMilli() or UnixMicro() or UnixNano() respectively, used to output time.Time.
package main
import (
"os"
"github.com/rambollwong/rainbowlog/log"
)
func main() {
logger := log.New(
log.WithDefault(),
log.AppendsEncoderWriters(log.JsonEnc, os.Stderr),
log.WithTimeFormat(log.TimeFormatUnix),
)
logger.Info().Msg("Hello world!").Done()
}
// Output:{"_TIME_":1708346689,"_LEVEL_":"INFO","_CALLER_":"main.go:16","message":"Hello world!"}
Hook
package main
import (
"fmt"
"os"
"github.com/rambollwong/rainbowlog/log"
"github.com/rambollwong/rainbowlog/level"
)
func main() {
var hook log.HookFunc = func(r log.Record, lv level.Level, message string) {
fmt.Printf("hook: %s, %s\n", lv.String(), message)
}
logger := log.New(
log.WithDefault(),
log.AppendsEncoderWriters(log.JsonEnc, os.Stderr),
log.WithCallerMarshalFunc(nil),
log.AppendsHooks(hook),
log.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!"}
Advanced Usage
BufferedWriter
RainbowLog provides BufferedWriter functionality, which can significantly improve log writing performance:
bufferedWriter := log.NewBufferedWriter(os.Stdout, 4096)
logger := log.New(
log.AppendsEncoderWriters(log.JsonEnc, bufferedWriter),
)
SyncWriter
For non-thread-safe Writers, you can use SyncWriter wrapper:
syncWriter := log.SyncWriter(os.Stdout)
logger := log.New(
log.AppendsEncoderWriters(log.JsonEnc, syncWriter),
)
Note: POSIX and Windows operating systems are inherently write-safe, no need to wrap with SyncWriter!
MultiWriter
Supports writing to multiple targets simultaneously:
multiWriter := log.MultiLevelWriter(os.Stdout, fileWriter)
logger := log.New(
log.AppendsEncoderWriters(log.JsonEnc, multiWriter),
)
Performance Optimization Recommendations
- Use Buffered Writer: For frequent log writing operations, using a buffered writer can significantly improve performance
- Set Log Levels Appropriately: Appropriately increase log levels in production environments to avoid too many debug logs affecting performance
- Avoid Recording Too Much Information in Hot Paths: Try to reduce the content and frequency of log recording on critical performance paths
- Use Object Pool: RainbowLog uses object pools internally to reduce memory allocation, ensure proper use of the
Done()method to release record objects
More
Of course, we provide more features and capabilities, looking forward to your exploration and discovery!
Contact Us
- Email:
ramboll.wong@hotmail.com - Telegram Technical Discussion Group: [Join Now]
- Blog:Ramboll's Blog
Support with a Donation
If you like this project, feel free to buy the author a cup of lemonade ☕️. Your support is my motivation for continuous updates!
- WeChat Pay:
- Alipay:
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
Package log implements a comprehensive structured logging solution with multiple writer implementations including buffered, synchronized, and multi-writer capabilities.
|
Package log implements a comprehensive structured logging solution with multiple writer implementations including buffered, synchronized, and multi-writer capabilities. |
|
Package logger provides the definition and initialization operation of a global rainbow logger.
|
Package logger provides the definition and initialization operation of a global rainbow logger. |