chore: set log to main (#23698)

This commit is contained in:
Tyler 2025-02-13 13:23:25 -08:00 committed by GitHub
parent 256e3e143b
commit 8abca32b6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 509 additions and 121 deletions

View File

@ -11,29 +11,55 @@ Mention whether you follow Semantic Versioning.
Usage:
Change log entries are to be added to the Unreleased section under the
appropriate stanza (see below). Each entry should ideally include a tag and
the Github issue reference in the following format:
Change log entries are to be added to the Unreleased section from newest to oldest.
Each entry must include the Github issue reference in the following format:
* (<tag>) [#<issue-number>] Changelog message.
* [#<issue-number>] Changelog message.
Types of changes (Stanzas):
"Features" for new features.
"Improvements" for changes in existing functionality.
"Deprecated" for soon-to-be removed features.
"Bug Fixes" for any bug fixes.
"API Breaking" for breaking exported APIs used by developers building on SDK.
Ref: https://keepachangelog.com/en/1.0.0/
-->
# Changelog
## [Unreleased]
### Features
## [v1.5.0](https://github.com/cosmos/cosmos-sdk/releases/tag/log/v1.4.1) - 2024-11-07
* [#15956](https://github.com/cosmos/cosmos-sdk/pull/15956) Introduce extra options to configure logger.
* [#22466](https://github.com/cosmos/cosmos-sdk/pull/22466) Disable coloring in testing logger.
* [#22233](https://github.com/cosmos/cosmos-sdk/pull/22233) Use sonic json library for faster json handling.
* [#22347](https://github.com/cosmos/cosmos-sdk/pull/22347) Add `cosmossdk.io/log/slog` to allow using a standard library log/slog-backed logger. This required to bump the minimum go version of `cosmossdk.io/log` to 1.21.
## [v1.4.1](https://github.com/cosmos/cosmos-sdk/releases/tag/log/v1.4.1) - 2024-08-16
* [#21326](https://github.com/cosmos/cosmos-sdk/pull/21326) Avoid context key collision.
## [v1.4.0](https://github.com/cosmos/cosmos-sdk/releases/tag/log/v1.4.0) - 2024-08-07
* [#21045](https://github.com/cosmos/cosmos-sdk/pull/21045) Add `WithContext` method implementations to make all returned loggers compatible with `cosmossdk.io/core/log.Logger` (v1) without a direct dependency.
## [v1.3.1](https://github.com/cosmos/cosmos-sdk/releases/tag/log/v1.3.1) - 2024-02-05
* [#19346](https://github.com/cosmos/cosmos-sdk/pull/19346) Upgrade zerolog to v1.32.0.
* [#19346](https://github.com/cosmos/cosmos-sdk/pull/19346) `#15956` now works thanks to the upgrade of `zerolog`.
## [v1.3.0](https://github.com/cosmos/cosmos-sdk/releases/tag/log/v1.3.0) - 2024-01-10
* [#18916](https://github.com/cosmos/cosmos-sdk/pull/18916) Introduce an option for setting hooks.
* [#18429](https://github.com/cosmos/cosmos-sdk/pull/18429) Support customization of log json marshal.
* [#18898](https://github.com/cosmos/cosmos-sdk/pull/18898) Add `WARN` level.
## [v1.2.1](https://github.com/cosmos/cosmos-sdk/releases/tag/log/v1.2.1) - 2023-08-25
* [#17532](https://github.com/cosmos/cosmos-sdk/pull/17532) Proper marshalling of `fmt.Stringer` (follow-up of [#17205](https://github.com/cosmos/cosmos-sdk/pull/17205)).
## [v1.2.0](https://github.com/cosmos/cosmos-sdk/releases/tag/log/v1.2.0) - 2023-07-31
* [#17194](https://github.com/cosmos/cosmos-sdk/pull/17194) Avoid repeating parse log level in `ParseLogLevel`.
* [#17205](https://github.com/cosmos/cosmos-sdk/pull/17205) Fix types that do not implement the `json.Marshaler` interface.
* [#15956](https://github.com/cosmos/cosmos-sdk/pull/15956) Introduce an option for enabling error stack trace.
## [v1.1.0](https://github.com/cosmos/cosmos-sdk/releases/tag/log/v1.1.0) - 2023-04-27
* [#15956](https://github.com/cosmos/cosmos-sdk/pull/15956) Introduce options to configure logger (enable/disable colored output, customize log timestamps).
## [v1.0.0](https://github.com/cosmos/cosmos-sdk/releases/tag/log/v1.0.0) - 2023-03-30

View File

@ -1,3 +1,5 @@
# Log
The `cosmossdk.io/log` provides a zerolog logging implementation for the Cosmos SDK and Cosmos SDK modules.
To use a logger wrapping an instance of the standard library's `log/slog` package, use `cosmossdk.io/log/slog`.

View File

@ -83,7 +83,6 @@ func BenchmarkLoggers(b *testing.B) {
// so that real write time is negligible.
b.Run("zerolog", func(b *testing.B) {
for _, bc := range benchCases {
bc := bc
b.Run(bc.name, func(b *testing.B) {
zl := zerolog.New(io.Discard)
logger := log.NewCustomLogger(zl)
@ -99,7 +98,6 @@ func BenchmarkLoggers(b *testing.B) {
// also useful as a reference for how expensive zerolog is.
b.Run("specialized nop logger", func(b *testing.B) {
for _, bc := range nopCases {
bc := bc
b.Run(bc.name, func(b *testing.B) {
logger := log.NewNopLogger()
@ -115,7 +113,6 @@ func BenchmarkLoggers(b *testing.B) {
// so we offer the specialized version in the exported API.
b.Run("zerolog nop logger", func(b *testing.B) {
for _, bc := range nopCases {
bc := bc
b.Run(bc.name, func(b *testing.B) {
logger := log.NewCustomLogger(zerolog.Nop())

View File

@ -3,13 +3,18 @@ module cosmossdk.io/log
go 1.23
require (
github.com/rs/zerolog v1.29.1
gotest.tools/v3 v3.4.0
github.com/bytedance/sonic v1.12.8
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.33.0
)
require (
github.com/google/go-cmp v0.5.5 // indirect
github.com/bytedance/sonic/loader v0.2.3 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
golang.org/x/sys v0.8.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
golang.org/x/arch v0.13.0 // indirect
golang.org/x/sys v0.29.0 // indirect
)

View File

@ -1,47 +1,52 @@
github.com/bytedance/sonic v1.12.8 h1:4xYRVRlXIgvSZ4e8iVTlMF5szgpXd4AfvuWgA8I8lgs=
github.com/bytedance/sonic v1.12.8/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=

View File

@ -35,7 +35,7 @@ func ParseLogLevel(levelStr string) (FilterFunc, error) {
}
// parse and validate the levels
filterMap := make(map[string]string)
filterMap := make(map[string]zerolog.Level)
list := strings.Split(l, ",")
for _, item := range list {
moduleAndLevel := strings.Split(item, ":")
@ -50,18 +50,19 @@ func ParseLogLevel(levelStr string) (FilterFunc, error) {
return nil, fmt.Errorf("duplicate module %s in log level list %s", module, list)
}
if _, err := zerolog.ParseLevel(level); err != nil {
zllevel, err := zerolog.ParseLevel(level)
if err != nil {
return nil, fmt.Errorf("invalid log level %s in log level list %s", level, list)
}
filterMap[module] = level
filterMap[module] = zllevel
}
filterFunc := func(key, lvl string) bool {
level, ok := filterMap[key]
zllevel, ok := filterMap[key]
if !ok { // no level filter for this key
// check if there is a default level filter
level, ok = filterMap[defaultLogLevelKey]
zllevel, ok = filterMap[defaultLogLevelKey]
if !ok {
return false
}
@ -72,11 +73,6 @@ func ParseLogLevel(levelStr string) (FilterFunc, error) {
panic(err)
}
zllevel, err := zerolog.ParseLevel(level)
if err != nil {
panic(err)
}
return zllvl < zllevel
}

View File

@ -3,46 +3,88 @@ package log_test
import (
"testing"
"gotest.tools/v3/assert"
"cosmossdk.io/log"
)
func TestParseLogLevel(t *testing.T) {
_, err := log.ParseLogLevel("")
assert.Error(t, err, "empty log level")
if err == nil {
t.Errorf("expected error for empty log level, got nil")
}
level := "consensus:foo,mempool:debug,*:error"
_, err = log.ParseLogLevel(level)
assert.Error(t, err, "invalid log level foo in log level list [consensus:foo mempool:debug *:error]")
if err == nil {
t.Errorf("expected error for invalid log level foo in log level list [consensus:foo mempool:debug *:error], got nil")
}
level = "consensus:debug,mempool:debug,*:error"
filter, err := log.ParseLogLevel(level)
assert.NilError(t, err)
assert.Assert(t, filter != nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if filter == nil {
t.Fatalf("expected non-nil filter, got nil")
}
assert.Assert(t, !filter("consensus", "debug"))
assert.Assert(t, !filter("consensus", "info"))
assert.Assert(t, !filter("consensus", "error"))
assert.Assert(t, !filter("mempool", "debug"))
assert.Assert(t, !filter("mempool", "info"))
assert.Assert(t, !filter("mempool", "error"))
assert.Assert(t, !filter("state", "error"))
assert.Assert(t, !filter("server", "panic"))
if filter("consensus", "debug") {
t.Errorf("expected filter to return false for consensus:debug")
}
if filter("consensus", "info") {
t.Errorf("expected filter to return false for consensus:info")
}
if filter("consensus", "error") {
t.Errorf("expected filter to return false for consensus:error")
}
if filter("mempool", "debug") {
t.Errorf("expected filter to return false for mempool:debug")
}
if filter("mempool", "info") {
t.Errorf("expected filter to return false for mempool:info")
}
if filter("mempool", "error") {
t.Errorf("expected filter to return false for mempool:error")
}
if filter("state", "error") {
t.Errorf("expected filter to return false for state:error")
}
if filter("server", "panic") {
t.Errorf("expected filter to return false for server:panic")
}
assert.Assert(t, filter("server", "debug"))
assert.Assert(t, filter("state", "debug"))
assert.Assert(t, filter("state", "info"))
if !filter("server", "debug") {
t.Errorf("expected filter to return true for server:debug")
}
if !filter("state", "debug") {
t.Errorf("expected filter to return true for state:debug")
}
if !filter("state", "info") {
t.Errorf("expected filter to return true for state:info")
}
level = "error"
filter, err = log.ParseLogLevel(level)
assert.NilError(t, err)
assert.Assert(t, filter != nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if filter == nil {
t.Fatalf("expected non-nil filter, got nil")
}
assert.Assert(t, !filter("state", "error"))
assert.Assert(t, !filter("consensus", "error"))
if filter("state", "error") {
t.Errorf("expected filter to return false for state:error")
}
if filter("consensus", "error") {
t.Errorf("expected filter to return false for consensus:error")
}
assert.Assert(t, filter("consensus", "debug"))
assert.Assert(t, filter("consensus", "info"))
assert.Assert(t, filter("state", "debug"))
if !filter("consensus", "debug") {
t.Errorf("expected filter to return true for consensus:debug")
}
if !filter("consensus", "info") {
t.Errorf("expected filter to return true for consensus:info")
}
if !filter("state", "debug") {
t.Errorf("expected filter to return true for state:debug")
}
}

View File

@ -1,25 +1,50 @@
package log
import (
"encoding"
"encoding/json"
"fmt"
"io"
"github.com/bytedance/sonic"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/rs/zerolog/pkgerrors"
)
func init() {
zerolog.InterfaceMarshalFunc = func(i any) ([]byte, error) {
switch v := i.(type) {
case json.Marshaler:
return sonic.Marshal(i)
case encoding.TextMarshaler:
return sonic.Marshal(i)
case fmt.Stringer:
return sonic.Marshal(v.String())
default:
return sonic.Marshal(i)
}
}
}
// ModuleKey defines a module logging key.
const ModuleKey = "module"
// ContextKey is used to store the logger in the context.
var ContextKey struct{}
var ContextKey contextKey
type contextKey struct{}
// Logger is the Cosmos SDK logger interface.
// It maintains as much backward compatibility with the CometBFT logger as possible.
// All functionalities of the logger are available through the Impl() method.
type Logger interface {
// Info takes a message and a set of key/value pairs and logs with level INFO.
// The key of the tuple must be a string.
Info(msg string, keyVals ...any)
// Warn takes a message and a set of key/value pairs and logs with level WARN.
// The key of the tuple must be a string.
Warn(msg string, keyVals ...any)
// Error takes a message and a set of key/value pairs and logs with level ERR.
// The key of the tuple must be a string.
Error(msg string, keyVals ...any)
@ -37,6 +62,22 @@ type Logger interface {
Impl() any
}
// WithJSONMarshal configures zerolog global json encoding.
func WithJSONMarshal(marshaler func(v any) ([]byte, error)) {
zerolog.InterfaceMarshalFunc = func(i any) ([]byte, error) {
switch v := i.(type) {
case json.Marshaler:
return marshaler(i)
case encoding.TextMarshaler:
return marshaler(i)
case fmt.Stringer:
return marshaler(v.String())
default:
return marshaler(i)
}
}
}
type zeroLogWrapper struct {
*zerolog.Logger
}
@ -69,6 +110,15 @@ func NewLogger(dst io.Writer, options ...Option) Logger {
}
logger := zerolog.New(output)
if logCfg.StackTrace {
zerolog.ErrorStackMarshaler = func(err error) interface{} {
return pkgerrors.MarshalStack(errors.WithStack(err))
}
logger = logger.With().Stack().Logger()
}
if logCfg.TimeFormat != "" {
logger = logger.With().Timestamp().Logger()
}
@ -77,6 +127,8 @@ func NewLogger(dst io.Writer, options ...Option) Logger {
logger = logger.Level(logCfg.Level)
}
logger = logger.Hook(logCfg.Hooks...)
return zeroLogWrapper{&logger}
}
@ -91,13 +143,19 @@ func (l zeroLogWrapper) Info(msg string, keyVals ...interface{}) {
l.Logger.Info().Fields(keyVals).Msg(msg)
}
// Error takes a message and a set of key/value pairs and logs with level DEBUG.
// Warn takes a message and a set of key/value pairs and logs with level WARN.
// The key of the tuple must be a string.
func (l zeroLogWrapper) Warn(msg string, keyVals ...interface{}) {
l.Logger.Warn().Fields(keyVals).Msg(msg)
}
// Error takes a message and a set of key/value pairs and logs with level ERROR.
// The key of the tuple must be a string.
func (l zeroLogWrapper) Error(msg string, keyVals ...interface{}) {
l.Logger.Error().Fields(keyVals).Msg(msg)
}
// Debug takes a message and a set of key/value pairs and logs with level ERR.
// Debug takes a message and a set of key/value pairs and logs with level DEBUG.
// The key of the tuple must be a string.
func (l zeroLogWrapper) Debug(msg string, keyVals ...interface{}) {
l.Logger.Debug().Fields(keyVals).Msg(msg)
@ -109,6 +167,12 @@ func (l zeroLogWrapper) With(keyVals ...interface{}) Logger {
return zeroLogWrapper{&logger}
}
// WithContext returns a new wrapped logger with additional context provided by a set.
func (l zeroLogWrapper) WithContext(keyVals ...interface{}) any {
logger := l.Logger.With().Fields(keyVals).Logger()
return zeroLogWrapper{&logger}
}
// Impl returns the underlying zerolog logger.
// It can be used to used zerolog structured API directly instead of the wrapper.
func (l zeroLogWrapper) Impl() interface{} {
@ -126,8 +190,10 @@ func NewNopLogger() Logger {
// The custom implementation is about 3x faster.
type nopLogger struct{}
func (nopLogger) Info(string, ...any) {}
func (nopLogger) Error(string, ...any) {}
func (nopLogger) Debug(string, ...any) {}
func (nopLogger) With(...any) Logger { return nopLogger{} }
func (nopLogger) Impl() any { return nopLogger{} }
func (nopLogger) Info(string, ...any) {}
func (nopLogger) Warn(string, ...any) {}
func (nopLogger) Error(string, ...any) {}
func (nopLogger) Debug(string, ...any) {}
func (nopLogger) With(...any) Logger { return nopLogger{} }
func (nopLogger) WithContext(...any) any { return nopLogger{} }
func (nopLogger) Impl() any { return nopLogger{} }

61
log/logger_test.go Normal file
View File

@ -0,0 +1,61 @@
package log_test
import (
"bytes"
"errors"
"strings"
"testing"
"github.com/rs/zerolog"
"cosmossdk.io/log"
)
func inner() error {
return errors.New("seems we have an error here")
}
type _MockHook string
func (h _MockHook) Run(e *zerolog.Event, l zerolog.Level, msg string) {
e.Bool(string(h), true)
}
func TestLoggerOptionHooks(t *testing.T) {
buf := new(bytes.Buffer)
var (
mockHook1 _MockHook = "mock_message1"
mockHook2 _MockHook = "mock_message2"
)
logger := log.NewLogger(buf, log.HooksOption(mockHook1, mockHook2), log.ColorOption(false))
logger.Info("hello world")
if !strings.Contains(buf.String(), "mock_message1=true") {
t.Fatalf("expected mock_message1=true, got: %s", buf.String())
}
if !strings.Contains(buf.String(), "mock_message2=true") {
t.Fatalf("expected mock_message2=true, got: %s", buf.String())
}
buf.Reset()
logger = log.NewLogger(buf, log.HooksOption(), log.ColorOption(false))
logger.Info("hello world")
if !strings.Contains(buf.String(), "hello world") {
t.Fatalf("expected hello world, got: %s", buf.String())
}
}
func TestLoggerOptionStackTrace(t *testing.T) {
buf := new(bytes.Buffer)
logger := log.NewLogger(buf, log.TraceOption(true), log.ColorOption(false))
logger.Error("this log should be displayed", "error", inner())
if strings.Count(buf.String(), "logger_test.go") != 1 {
t.Fatalf("stack trace not found, got: %s", buf.String())
}
buf.Reset()
logger = log.NewLogger(buf, log.TraceOption(false), log.ColorOption(false))
logger.Error("this log should be displayed", "error", inner())
if strings.Count(buf.String(), "logger_test.go") > 0 {
t.Fatalf("stack trace found, got: %s", buf.String())
}
}

View File

@ -12,7 +12,9 @@ var defaultConfig = Config{
Filter: nil,
OutputJSON: false,
Color: true,
StackTrace: false,
TimeFormat: time.Kitchen,
Hooks: nil,
}
// Config defines configuration for the logger.
@ -21,7 +23,9 @@ type Config struct {
Filter FilterFunc
OutputJSON bool
Color bool
StackTrace bool
TimeFormat string
Hooks []zerolog.Hook
}
type Option func(*Config)
@ -78,3 +82,17 @@ func TimeFormatOption(format string) Option {
cfg.TimeFormat = format
}
}
// TraceOption add option to enable/disable print of stacktrace on error log
func TraceOption(val bool) Option {
return func(cfg *Config) {
cfg.StackTrace = val
}
}
// HooksOption append hooks to the Logger hooks
func HooksOption(hooks ...zerolog.Hook) Option {
return func(cfg *Config) {
cfg.Hooks = append(cfg.Hooks, hooks...)
}
}

50
log/slog/logger.go Normal file
View File

@ -0,0 +1,50 @@
// Package slog contains a Logger type that satisfies [cosmossdk.io/log.Logger],
// backed by a standard library [*log/slog.Logger].
package slog
import (
"log/slog"
"cosmossdk.io/log"
)
var _ log.Logger = Logger{}
// Logger satisfies [log.Logger] with logging backed by
// an instance of [*slog.Logger].
type Logger struct {
log *slog.Logger
}
// NewCustomLogger returns a Logger backed by an existing slog.Logger instance.
// All logging methods are called directly on the *slog.Logger;
// therefore it is the caller's responsibility to configure message filtering,
// level filtering, output format, and so on.
func NewCustomLogger(log *slog.Logger) Logger {
return Logger{log: log}
}
func (l Logger) Info(msg string, keyVals ...any) {
l.log.Info(msg, keyVals...)
}
func (l Logger) Warn(msg string, keyVals ...any) {
l.log.Warn(msg, keyVals...)
}
func (l Logger) Error(msg string, keyVals ...any) {
l.log.Error(msg, keyVals...)
}
func (l Logger) Debug(msg string, keyVals ...any) {
l.log.Debug(msg, keyVals...)
}
func (l Logger) With(keyVals ...any) log.Logger {
return Logger{log: l.log.With(keyVals...)}
}
// Impl returns l's underlying [*slog.Logger].
func (l Logger) Impl() any {
return l.log
}

92
log/slog/logger_test.go Normal file
View File

@ -0,0 +1,92 @@
package slog_test
import (
"bytes"
"encoding/json"
stdslog "log/slog"
"testing"
"cosmossdk.io/log/slog"
)
func TestSlog(t *testing.T) {
var buf bytes.Buffer
h := stdslog.NewJSONHandler(&buf, &stdslog.HandlerOptions{
Level: stdslog.LevelDebug,
})
logger := slog.NewCustomLogger(stdslog.New(h))
type logLine struct {
Level string `json:"level"`
Msg string `json:"msg"`
Num int `json:"num"`
}
var line logLine
logger.Debug("Message one", "num", 1)
if err := json.Unmarshal(buf.Bytes(), &line); err != nil {
t.Fatal(err)
}
if want := (logLine{
Level: stdslog.LevelDebug.String(),
Msg: "Message one",
Num: 1,
}); want != line {
t.Fatalf("unexpected log record: want %v, got %v", want, line)
}
buf.Reset()
logger.Info("Message two", "num", 2)
if err := json.Unmarshal(buf.Bytes(), &line); err != nil {
t.Fatal(err)
}
if want := (logLine{
Level: stdslog.LevelInfo.String(),
Msg: "Message two",
Num: 2,
}); want != line {
t.Fatalf("unexpected log record: want %v, got %v", want, line)
}
buf.Reset()
logger.Warn("Message three", "num", 3)
if err := json.Unmarshal(buf.Bytes(), &line); err != nil {
t.Fatal(err)
}
if want := (logLine{
Level: stdslog.LevelWarn.String(),
Msg: "Message three",
Num: 3,
}); want != line {
t.Fatalf("unexpected log record: want %v, got %v", want, line)
}
buf.Reset()
logger.Error("Message four", "num", 4)
if err := json.Unmarshal(buf.Bytes(), &line); err != nil {
t.Fatal(err)
}
if want := (logLine{
Level: stdslog.LevelError.String(),
Msg: "Message four",
Num: 4,
}); want != line {
t.Fatalf("unexpected log record: want %v, got %v", want, line)
}
wLogger := logger.With("num", 5)
buf.Reset()
wLogger.Info("Using .With")
if err := json.Unmarshal(buf.Bytes(), &line); err != nil {
t.Fatal(err)
}
if want := (logLine{
Level: stdslog.LevelInfo.String(),
Msg: "Using .With",
Num: 5,
}); want != line {
t.Fatalf("unexpected log record: want %v, got %v", want, line)
}
}

View File

@ -0,0 +1,16 @@
sonar.projectKey=cosmos-sdk-log
sonar.organization=cosmos
sonar.projectName=Cosmos SDK - Log
sonar.project.monorepo.enabled=true
sonar.sources=.
sonar.exclusions=**/*_test.go,**/*.pb.go,**/*.pulsar.go,**/*.pb.gw.go
sonar.coverage.exclusions=**/*_test.go,**/testutil/**,**/*.pb.go,**/*.pb.gw.go,**/*.pulsar.go,test_helpers.go,docs/**
sonar.tests=.
sonar.test.inclusions=**/*_test.go
sonar.go.coverage.reportPaths=coverage.out
sonar.sourceEncoding=UTF-8
sonar.scm.provider=git
sonar.scm.forceReloadAll=true

View File

@ -1,6 +1,10 @@
package log
import "github.com/rs/zerolog"
import (
"time"
"github.com/rs/zerolog"
)
// TestingT is the interface required for logging in tests.
// It is a subset of testing.T to avoid a direct dependency on the testing package.
@ -39,15 +43,18 @@ func NewTestLoggerError(t TestingT) Logger {
}
func newTestLogger(t TestingT, lvl zerolog.Level) Logger {
cw := zerolog.NewConsoleWriter()
cw.Out = zerolog.TestWriter{
T: t,
// Normally one would use zerolog.ConsoleTestWriter
// to set the option on NewConsoleWriter,
// but the zerolog source for that is hardcoded to Frame=6.
// With Frame=6, all source locations are printed as "logger.go",
// but Frame=7 prints correct source locations.
Frame: 7,
cw := zerolog.ConsoleWriter{
NoColor: true,
TimeFormat: time.Kitchen,
Out: zerolog.TestWriter{
T: t,
// Normally one would use zerolog.ConsoleTestWriter
// to set the option on NewConsoleWriter,
// but the zerolog source for that is hardcoded to Frame=6.
// With Frame=6, all source locations are printed as "logger.go",
// but Frame=7 prints correct source locations.
Frame: 7,
},
}
return NewCustomLogger(zerolog.New(cw).Level(lvl))
}

View File

@ -1,9 +1,10 @@
package log
import (
"encoding/json"
"fmt"
"io"
"github.com/bytedance/sonic"
)
// NewFilterWriter returns a writer that filters out all key/value pairs that do not match the filter.
@ -28,7 +29,7 @@ func (fw *filterWriter) Write(p []byte) (n int, err error) {
Module string `json:"module"`
}
if err := json.Unmarshal(p, &event); err != nil {
if err := sonic.Unmarshal(p, &event); err != nil {
return 0, fmt.Errorf("failed to unmarshal event: %w", err)
}

View File

@ -5,8 +5,6 @@ import (
"strings"
"testing"
"gotest.tools/v3/assert"
"cosmossdk.io/log"
)
@ -15,13 +13,19 @@ func TestFilteredWriter(t *testing.T) {
level := "consensus:debug,mempool:debug,*:error"
filter, err := log.ParseLogLevel(level)
assert.NilError(t, err)
if err != nil {
t.Fatalf("failed to parse log level: %v", err)
}
logger := log.NewLogger(buf, log.FilterOption(filter))
logger.Debug("this log line should be displayed", log.ModuleKey, "consensus")
assert.Check(t, strings.Contains(buf.String(), "this log line should be displayed"))
if !strings.Contains(buf.String(), "this log line should be displayed") {
t.Errorf("expected log line to be displayed, but it was not")
}
buf.Reset()
logger.Debug("this log line should be filtered", log.ModuleKey, "server")
assert.Check(t, buf.Len() == 0)
if buf.Len() != 0 {
t.Errorf("expected log line to be filtered, but it was not")
}
}