From e699254142bae4c1a120d10e86f7bcce206b2cd9 Mon Sep 17 00:00:00 2001 From: Evgeny Danilenko <6655321@bk.ru> Date: Tue, 9 Aug 2022 22:11:09 +0300 Subject: [PATCH] Use atomic pointer in go 1.19 (#446) * use atomic pointer * golang version * golang version * go1.19 * linters * Bump golangci-lint * linters * linters * linters after merge * generic logger * generic logger * logger * logger * linters * bump toml * linters1 * linters * linters * linter * linter * linters * linters * linters --- .github/workflows/ci.yml | 2 +- .golangci.yml | 1 + Makefile | 2 +- accounts/abi/bind/auth.go | 8 ++- accounts/abi/bind/bind_test.go | 7 +- cmd/geth/config.go | 6 +- consensus/bor/bor.go | 61 +++++++++++------- consensus/bor/heimdallgrpc/state_sync.go | 16 +++-- eth/downloader/downloader.go | 10 +-- eth/filters/test_backend.go | 6 +- go.mod | 2 +- internal/cli/server/config_legacy.go | 4 +- internal/testlog/testlog.go | 10 ++- log/handler.go | 81 +++++++++++++----------- log/handler_glog.go | 16 +++-- log/handler_go119.go | 27 ++++++++ log/handler_go14.go | 3 +- log/logger.go | 7 +- log/syslog.go | 2 +- p2p/discover/v4_udp_test.go | 2 +- p2p/discover/v5_udp_test.go | 2 +- 21 files changed, 175 insertions(+), 100 deletions(-) create mode 100644 log/handler_go119.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac40c5e37dd5..a85cb300d255 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: - uses: actions/setup-go@v3 with: - go-version: 1.18.x + go-version: 1.19.x - name: Install dependencies on Linux if: runner.os == 'Linux' diff --git a/.golangci.yml b/.golangci.yml index 89a9e328b84d..89eebfe9feba 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -50,6 +50,7 @@ linters: - unconvert - unparam - wsl + - asasalint #- errorlint causes stack overflow. TODO: recheck after each golangci update linters-settings: diff --git a/Makefile b/Makefile index b76bbf25ee74..6e1e472a03d9 100644 --- a/Makefile +++ b/Makefile @@ -75,7 +75,7 @@ lint: lintci-deps: rm -f ./build/bin/golangci-lint - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./build/bin v1.46.0 + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./build/bin v1.48.0 goimports: goimports -local "$(PACKAGE)" -w . diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go index a4307a952910..63e6f624519c 100644 --- a/accounts/abi/bind/auth.go +++ b/accounts/abi/bind/auth.go @@ -21,7 +21,6 @@ import ( "crypto/ecdsa" "errors" "io" - "io/ioutil" "math/big" "github.com/ethereum/go-ethereum/accounts" @@ -45,14 +44,17 @@ var ErrNotAuthorized = errors.New("not authorized to sign this account") // Deprecated: Use NewTransactorWithChainID instead. func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) { log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID") - json, err := ioutil.ReadAll(keyin) + + json, err := io.ReadAll(keyin) if err != nil { return nil, err } + key, err := keystore.DecryptKey(json, passphrase) if err != nil { return nil, err } + return NewKeyedTransactor(key.PrivateKey), nil } @@ -106,7 +108,7 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { // NewTransactorWithChainID is a utility method to easily create a transaction signer from // an encrypted json key stream and the associated passphrase. func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) { - json, err := ioutil.ReadAll(keyin) + json, err := io.ReadAll(keyin) if err != nil { return nil, err } diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 992497993ad3..644c111f0891 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -18,7 +18,6 @@ package bind import ( "fmt" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -1966,7 +1965,7 @@ func TestGolangBindings(t *testing.T) { t.Skip("go sdk not found for testing") } // Create a temporary workspace for the test suite - ws, err := ioutil.TempDir("", "binding-test") + ws, err := os.MkdirTemp("", "binding-test") if err != nil { t.Fatalf("failed to create temporary workspace: %v", err) } @@ -1990,7 +1989,7 @@ func TestGolangBindings(t *testing.T) { if err != nil { t.Fatalf("test %d: failed to generate binding: %v", i, err) } - if err = ioutil.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+".go"), []byte(bind), 0600); err != nil { + if err = os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+".go"), []byte(bind), 0600); err != nil { t.Fatalf("test %d: failed to write binding: %v", i, err) } // Generate the test file with the injected test code @@ -2006,7 +2005,7 @@ func TestGolangBindings(t *testing.T) { %s } `, tt.imports, tt.name, tt.tester) - if err := ioutil.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+"_test.go"), []byte(code), 0600); err != nil { + if err := os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+"_test.go"), []byte(code), 0600); err != nil { t.Fatalf("test %d: failed to write tests: %v", i, err) } }) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 08b76f83daed..e6cb36b1212b 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -18,14 +18,12 @@ package main import ( "fmt" - "io/ioutil" "math/big" "os" "time" - "gopkg.in/urfave/cli.v1" - "github.com/BurntSushi/toml" + "gopkg.in/urfave/cli.v1" "github.com/ethereum/go-ethereum/accounts/external" "github.com/ethereum/go-ethereum/accounts/keystore" @@ -71,7 +69,7 @@ type gethConfig struct { } func loadConfig(file string, cfg *gethConfig) error { - data, err := ioutil.ReadFile(file) + data, err := os.ReadFile(file) if err != nil { return err } diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index dd3cff56fb5e..a8fe99375ca0 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -12,6 +12,7 @@ import ( "sort" "strconv" "sync" + "sync/atomic" "time" lru "github.com/hashicorp/golang-lru" @@ -97,6 +98,9 @@ var ( // errOutOfRangeChain is returned if an authorization list is attempted to // be modified via out-of-range or non-contiguous headers. errOutOfRangeChain = errors.New("out of range or non-contiguous chain") + + errUncleDetected = errors.New("uncles not allowed") + errUnknownValidators = errors.New("unknown validators") ) // SignerFn is a signer callback function to request a header to be signed by a @@ -210,9 +214,7 @@ type Bor struct { recents *lru.ARCCache // Snapshots for recent block to speed up reorgs signatures *lru.ARCCache // Signatures of recent blocks to speed up mining - signer common.Address // Ethereum address of the signing key - signFn SignerFn // Signer function to authorize hashes with - lock sync.RWMutex // Protects the signer fields + authorizedSigner atomic.Pointer[signer] // Ethereum address and sign function of the signing key ethAPI api.Caller spanner Spanner @@ -225,6 +227,11 @@ type Bor struct { closeOnce sync.Once } +type signer struct { + signer common.Address // Ethereum address of the signing key + signFn SignerFn // Signer function to authorize hashes with +} + // New creates a Matic Bor consensus engine. func New( chainConfig *params.ChainConfig, @@ -257,6 +264,14 @@ func New( HeimdallClient: heimdallClient, } + c.authorizedSigner.Store(&signer{ + common.Address{}, + func(_ accounts.Account, _ string, i []byte) ([]byte, error) { + // return an error to prevent panics + return nil, &UnauthorizedSignerError{0, common.Address{}.Bytes()} + }, + }) + // make sure we can decode all the GenesisAlloc in the BorConfig. for key, genesisAlloc := range c.config.BlockAlloc { if _, err := decodeGenesisAlloc(genesisAlloc); err != nil { @@ -572,7 +587,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co // uncles as this consensus mechanism doesn't permit uncles. func (c *Bor) VerifyUncles(_ consensus.ChainReader, block *types.Block) error { if len(block.Uncles()) > 0 { - return errors.New("uncles not allowed") + return errUncleDetected } return nil @@ -656,8 +671,10 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e return err } + currentSigner := *c.authorizedSigner.Load() + // Set the correct difficulty - header.Difficulty = new(big.Int).SetUint64(Difficulty(snap.ValidatorSet, c.signer)) + header.Difficulty = new(big.Int).SetUint64(Difficulty(snap.ValidatorSet, currentSigner.signer)) // Ensure the extra data has all it's components if len(header.Extra) < extraVanity { @@ -670,7 +687,7 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e if IsSprintStart(number+1, c.config.Sprint) { newValidators, err := c.spanner.GetCurrentValidators(context.Background(), header.ParentHash, number+1) if err != nil { - return errors.New("unknown validators") + return errUnknownValidators } // sort validator by address @@ -695,8 +712,8 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e var succession int // if signer is not empty - if c.signer != (common.Address{}) { - succession, err = snap.GetSignerSuccessionNumber(c.signer) + if currentSigner.signer != (common.Address{}) { + succession, err = snap.GetSignerSuccessionNumber(currentSigner.signer) if err != nil { return err } @@ -774,7 +791,7 @@ func (c *Bor) changeContractCodeIfNeeded(headerNumber uint64, state *state.State if blockNumber == strconv.FormatUint(headerNumber, 10) { allocs, err := decodeGenesisAlloc(genesisAlloc) if err != nil { - return fmt.Errorf("failed to decode genesis alloc: %v", err) + return fmt.Errorf("failed to decode genesis alloc: %w", err) } for addr, account := range allocs { @@ -838,12 +855,11 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ // Authorize injects a private key into the consensus engine to mint new blocks // with. -func (c *Bor) Authorize(signer common.Address, signFn SignerFn) { - c.lock.Lock() - defer c.lock.Unlock() - - c.signer = signer - c.signFn = signFn +func (c *Bor) Authorize(currentSigner common.Address, signFn SignerFn) { + c.authorizedSigner.Store(&signer{ + signer: currentSigner, + signFn: signFn, + }) } // Seal implements consensus.Engine, attempting to create a sealed block using @@ -860,10 +876,9 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result log.Info("Sealing paused, waiting for transactions") return nil } + // Don't hold the signer fields for the entire sealing procedure - c.lock.RLock() - signer, signFn := c.signer, c.signFn - c.lock.RUnlock() + currentSigner := *c.authorizedSigner.Load() snap, err := c.snapshot(chain, number-1, header.ParentHash, nil) if err != nil { @@ -871,12 +886,12 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result } // Bail out if we're unauthorized to sign a block - if !snap.ValidatorSet.HasAddress(signer) { + if !snap.ValidatorSet.HasAddress(currentSigner.signer) { // Check the UnauthorizedSignerError.Error() msg to see why we pass number-1 - return &UnauthorizedSignerError{number - 1, signer.Bytes()} + return &UnauthorizedSignerError{number - 1, currentSigner.signer.Bytes()} } - successionNumber, err := snap.GetSignerSuccessionNumber(signer) + successionNumber, err := snap.GetSignerSuccessionNumber(currentSigner.signer) if err != nil { return err } @@ -887,7 +902,7 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result wiggle := time.Duration(successionNumber) * time.Duration(c.config.CalculateBackupMultiplier(number)) * time.Second // Sign all the things! - err = Sign(signFn, signer, header, c.config) + err = Sign(currentSigner.signFn, currentSigner.signer, header, c.config) if err != nil { return err } @@ -949,7 +964,7 @@ func (c *Bor) CalcDifficulty(chain consensus.ChainHeaderReader, _ uint64, parent return nil } - return new(big.Int).SetUint64(Difficulty(snap.ValidatorSet, c.signer)) + return new(big.Int).SetUint64(Difficulty(snap.ValidatorSet, c.authorizedSigner.Load().signer)) } // SealHash returns the hash of a block prior to it being sealed. diff --git a/consensus/bor/heimdallgrpc/state_sync.go b/consensus/bor/heimdallgrpc/state_sync.go index aa10f0c5c3f9..910fb6c4ee5d 100644 --- a/consensus/bor/heimdallgrpc/state_sync.go +++ b/consensus/bor/heimdallgrpc/state_sync.go @@ -3,10 +3,10 @@ package heimdallgrpc import ( "context" + proto "github.com/maticnetwork/polyproto/heimdall" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/bor/clerk" - - proto "github.com/maticnetwork/polyproto/heimdall" ) func (h *HeimdallGRPCClient) StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) { @@ -18,13 +18,19 @@ func (h *HeimdallGRPCClient) StateSyncEvents(ctx context.Context, fromID uint64, Limit: uint64(stateFetchLimit), } - res, err := h.client.StateSyncEvents(ctx, req) + var ( + res proto.Heimdall_StateSyncEventsClient + events *proto.StateSyncEventsResponse + err error + ) + + res, err = h.client.StateSyncEvents(ctx, req) if err != nil { return nil, err } for { - events, err := res.Recv() + events, err = res.Recv() if err != nil { break } @@ -45,5 +51,5 @@ func (h *HeimdallGRPCClient) StateSyncEvents(ctx context.Context, fromID uint64, } } - return eventRecords, nil + return eventRecords, err } diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 34b8c9571521..f92bc652a617 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -207,7 +207,7 @@ type BlockChain interface { } // New creates a new downloader to fetch hashes and blocks from remote peers. -//nolint: staticcheck +// nolint: staticcheck func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn, success func(), whitelistService ethereum.ChainValidator) *Downloader { if lightchain == nil { lightchain = chain @@ -729,9 +729,11 @@ func (d *Downloader) fetchHead(p *peerConnection) (head *types.Header, pivot *ty // calculateRequestSpan calculates what headers to request from a peer when trying to determine the // common ancestor. // It returns parameters to be used for peer.RequestHeadersByNumber: -// from - starting block number -// count - number of headers to request -// skip - number of headers to skip +// +// from - starting block number +// count - number of headers to request +// skip - number of headers to skip +// // and also returns 'max', the last block which is expected to be returned by the remote peers, // given the (from,count,skip) func calculateRequestSpan(remoteHeight, localHeight uint64) (int64, int, int, uint64) { diff --git a/eth/filters/test_backend.go b/eth/filters/test_backend.go index c6496f4a084d..979ed3efb6f5 100644 --- a/eth/filters/test_backend.go +++ b/eth/filters/test_backend.go @@ -48,7 +48,11 @@ func (b *TestBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash) func (b *TestBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) { receipt, err := b.GetBorBlockReceipt(ctx, hash) - if receipt == nil || err != nil { + if err != nil { + return []*types.Log{}, err + } + + if receipt == nil { return []*types.Log{}, nil } diff --git a/go.mod b/go.mod index f6a37b20ba7e..2f429ed82dd3 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ethereum/go-ethereum -go 1.18 +go 1.19 require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 diff --git a/internal/cli/server/config_legacy.go b/internal/cli/server/config_legacy.go index 9411b8290d8d..ccc05eb4a7e7 100644 --- a/internal/cli/server/config_legacy.go +++ b/internal/cli/server/config_legacy.go @@ -2,13 +2,13 @@ package server import ( "fmt" - "io/ioutil" + "os" "github.com/BurntSushi/toml" ) func readLegacyConfig(path string) (*Config, error) { - data, err := ioutil.ReadFile(path) + data, err := os.ReadFile(path) tomlData := string(data) if err != nil { diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go index 684339f16d80..a5836b8446c6 100644 --- a/internal/testlog/testlog.go +++ b/internal/testlog/testlog.go @@ -26,12 +26,13 @@ import ( // Handler returns a log handler which logs to the unit test log of t. func Handler(t *testing.T, level log.Lvl) log.Handler { - return log.LvlFilterHandler(level, &handler{t, log.TerminalFormat(false)}) + return log.LvlFilterHandler(level, &handler{t, log.TerminalFormat(false), level}) } type handler struct { t *testing.T fmt log.Format + lvl log.Lvl } func (h *handler) Log(r *log.Record) error { @@ -39,6 +40,10 @@ func (h *handler) Log(r *log.Record) error { return nil } +func (h *handler) Level() log.Lvl { + return h.lvl +} + // logger implements log.Logger such that all output goes to the unit test log via // t.Logf(). All methods in between logger.Trace, logger.Debug, etc. are marked as test // helpers, so the file and line number in unit test output correspond to the call site @@ -59,6 +64,9 @@ func (h *bufHandler) Log(r *log.Record) error { h.buf = append(h.buf, r) return nil } +func (h *bufHandler) Level() log.Lvl { + return log.LvlTrace +} // Logger returns a logger which logs to the unit test log of t. func Logger(t *testing.T, level log.Lvl) log.Logger { diff --git a/log/handler.go b/log/handler.go index 4ad433334ed9..6e89858f4b59 100644 --- a/log/handler.go +++ b/log/handler.go @@ -17,18 +17,26 @@ import ( // them to achieve the logging structure that suits your applications. type Handler interface { Log(r *Record) error + Level() Lvl } // FuncHandler returns a Handler that logs records with the given // function. -func FuncHandler(fn func(r *Record) error) Handler { - return funcHandler(fn) +func FuncHandler(fn func(r *Record) error, lvl Lvl) Handler { + return funcHandler{fn, lvl} } -type funcHandler func(r *Record) error +type funcHandler struct { + log func(r *Record) error + lvl Lvl +} func (h funcHandler) Log(r *Record) error { - return h(r) + return h.log(r) +} + +func (h funcHandler) Level() Lvl { + return h.lvl } // StreamHandler writes log records to an io.Writer @@ -42,7 +50,7 @@ func StreamHandler(wr io.Writer, fmtr Format) Handler { h := FuncHandler(func(r *Record) error { _, err := wr.Write(fmtr.Format(r)) return err - }) + }, LvlTrace) return LazyHandler(SyncHandler(h)) } @@ -55,7 +63,7 @@ func SyncHandler(h Handler) Handler { defer mu.Unlock() mu.Lock() return h.Log(r) - }) + }, h.Level()) } // FileHandler returns a handler which writes log records to the give file @@ -99,7 +107,7 @@ func CallerFileHandler(h Handler) Handler { return FuncHandler(func(r *Record) error { r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call)) return h.Log(r) - }) + }, h.Level()) } // CallerFuncHandler returns a Handler that adds the calling function name to @@ -108,7 +116,7 @@ func CallerFuncHandler(h Handler) Handler { return FuncHandler(func(r *Record) error { r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call)) return h.Log(r) - }) + }, h.Level()) } // This function is here to please go vet on Go < 1.8. @@ -128,29 +136,28 @@ func CallerStackHandler(format string, h Handler) Handler { r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s)) } return h.Log(r) - }) + }, h.Level()) } // FilterHandler returns a Handler that only writes records to the // wrapped Handler if the given function evaluates true. For example, // to only log records where the 'err' key is not nil: // -// logger.SetHandler(FilterHandler(func(r *Record) bool { -// for i := 0; i < len(r.Ctx); i += 2 { -// if r.Ctx[i] == "err" { -// return r.Ctx[i+1] != nil -// } -// } -// return false -// }, h)) -// +// logger.SetHandler(FilterHandler(func(r *Record) bool { +// for i := 0; i < len(r.Ctx); i += 2 { +// if r.Ctx[i] == "err" { +// return r.Ctx[i+1] != nil +// } +// } +// return false +// }, h)) func FilterHandler(fn func(r *Record) bool, h Handler) Handler { return FuncHandler(func(r *Record) error { if fn(r) { return h.Log(r) } return nil - }) + }, h.Level()) } // MatchFilterHandler returns a Handler that only writes records @@ -158,8 +165,7 @@ func FilterHandler(fn func(r *Record) bool, h Handler) Handler { // context matches the value. For example, to only log records // from your ui package: // -// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler) -// +// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler) func MatchFilterHandler(key string, value interface{}, h Handler) Handler { return FilterHandler(func(r *Record) (pass bool) { switch key { @@ -185,8 +191,7 @@ func MatchFilterHandler(key string, value interface{}, h Handler) Handler { // level to the wrapped Handler. For example, to only // log Error/Crit records: // -// log.LvlFilterHandler(log.LvlError, log.StdoutHandler) -// +// log.LvlFilterHandler(log.LvlError, log.StdoutHandler) func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { return FilterHandler(func(r *Record) (pass bool) { return r.Lvl <= maxLvl @@ -198,10 +203,9 @@ func LvlFilterHandler(maxLvl Lvl, h Handler) Handler { // to different locations. For example, to log to a file and // standard error: // -// log.MultiHandler( -// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), -// log.StderrHandler) -// +// log.MultiHandler( +// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), +// log.StderrHandler) func MultiHandler(hs ...Handler) Handler { return FuncHandler(func(r *Record) error { for _, h := range hs { @@ -209,7 +213,7 @@ func MultiHandler(hs ...Handler) Handler { h.Log(r) } return nil - }) + }, LvlDebug) } // FailoverHandler writes all log records to the first handler @@ -219,10 +223,10 @@ func MultiHandler(hs ...Handler) Handler { // to writing to a file if the network fails, and then to // standard out if the file write fails: // -// log.FailoverHandler( -// log.Must.NetHandler("tcp", ":9090", log.JSONFormat()), -// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), -// log.StdoutHandler) +// log.FailoverHandler( +// log.Must.NetHandler("tcp", ":9090", log.JSONFormat()), +// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()), +// log.StdoutHandler) // // All writes that do not go to the first handler will add context with keys of // the form "failover_err_{idx}" which explain the error encountered while @@ -239,17 +243,17 @@ func FailoverHandler(hs ...Handler) Handler { } return err - }) + }, LvlTrace) } // ChannelHandler writes all records to the given channel. // It blocks if the channel is full. Useful for async processing // of log messages, it's used by BufferedHandler. -func ChannelHandler(recs chan<- *Record) Handler { +func ChannelHandler(recs chan<- *Record, lvl Lvl) Handler { return FuncHandler(func(r *Record) error { recs <- r return nil - }) + }, lvl) } // BufferedHandler writes all records to a buffered @@ -264,7 +268,8 @@ func BufferedHandler(bufSize int, h Handler) Handler { _ = h.Log(m) } }() - return ChannelHandler(recs) + + return ChannelHandler(recs, h.Level()) } // LazyHandler writes all values to the wrapped handler after evaluating @@ -297,7 +302,7 @@ func LazyHandler(h Handler) Handler { } return h.Log(r) - }) + }, h.Level()) } func evaluateLazy(lz Lazy) (interface{}, error) { @@ -333,7 +338,7 @@ func evaluateLazy(lz Lazy) (interface{}, error) { func DiscardHandler() Handler { return FuncHandler(func(r *Record) error { return nil - }) + }, LvlDiscard) } // Must provides the following Handler creation functions diff --git a/log/handler_glog.go b/log/handler_glog.go index 9b1d4efaf46e..67376d3d41fb 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -82,14 +82,14 @@ func (h *GlogHandler) Verbosity(level Lvl) { // // For instance: // -// pattern="gopher.go=3" -// sets the V level to 3 in all Go files named "gopher.go" +// pattern="gopher.go=3" +// sets the V level to 3 in all Go files named "gopher.go" // -// pattern="foo=3" -// sets V to 3 in all files of any packages whose import path ends in "foo" +// pattern="foo=3" +// sets V to 3 in all files of any packages whose import path ends in "foo" // -// pattern="foo/*=3" -// sets V to 3 in all files of any packages whose import path contains "foo" +// pattern="foo/*=3" +// sets V to 3 in all files of any packages whose import path contains "foo" func (h *GlogHandler) Vmodule(ruleset string) error { var filter []pattern for _, rule := range strings.Split(ruleset, ",") { @@ -230,3 +230,7 @@ func (h *GlogHandler) Log(r *Record) error { } return nil } + +func (h *GlogHandler) Level() Lvl { + return Lvl(atomic.LoadUint32(&h.level)) +} diff --git a/log/handler_go119.go b/log/handler_go119.go new file mode 100644 index 000000000000..843dfd83b0fc --- /dev/null +++ b/log/handler_go119.go @@ -0,0 +1,27 @@ +//+go:build go1.19 + +package log + +import "sync/atomic" + +// swapHandler wraps another handler that may be swapped out +// dynamically at runtime in a thread-safe fashion. +type swapHandler struct { + handler atomic.Pointer[Handler] +} + +func (h *swapHandler) Log(r *Record) error { + return (*h.handler.Load()).Log(r) +} + +func (h *swapHandler) Swap(newHandler Handler) { + h.handler.Store(&newHandler) +} + +func (h *swapHandler) Get() Handler { + return *h.handler.Load() +} + +func (h *swapHandler) Level() Lvl { + return (*h.handler.Load()).Level() +} diff --git a/log/handler_go14.go b/log/handler_go14.go index d0cb14aa063b..c02a9abd1639 100644 --- a/log/handler_go14.go +++ b/log/handler_go14.go @@ -1,5 +1,4 @@ -//go:build go1.4 -// +build go1.4 +//go:build !go1.19 package log diff --git a/log/logger.go b/log/logger.go index 276d6969e24d..2b96681a82bf 100644 --- a/log/logger.go +++ b/log/logger.go @@ -18,7 +18,8 @@ const skipLevel = 2 type Lvl int const ( - LvlCrit Lvl = iota + LvlDiscard Lvl = -1 + LvlCrit Lvl = iota LvlError LvlWarn LvlInfo @@ -131,6 +132,10 @@ type logger struct { } func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) { + if l.h.Level() < lvl { + return + } + l.h.Log(&Record{ Time: time.Now(), Lvl: lvl, diff --git a/log/syslog.go b/log/syslog.go index 451d831b6dd1..cfa7c8cc1b9a 100644 --- a/log/syslog.go +++ b/log/syslog.go @@ -45,7 +45,7 @@ func sharedSyslog(fmtr Format, sysWr *syslog.Writer, err error) (Handler, error) s := strings.TrimSpace(string(fmtr.Format(r))) return syslogFn(s) - }) + }, LvlTrace) return LazyHandler(&closingHandler{sysWr, h}), nil } diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go index e36912f010ae..e5e81dbb9969 100644 --- a/p2p/discover/v4_udp_test.go +++ b/p2p/discover/v4_udp_test.go @@ -562,7 +562,7 @@ func startLocalhostV4(t *testing.T, cfg Config) *UDPv4 { cfg.Log.SetHandler(log.FuncHandler(func(r *log.Record) error { t.Logf("%s %s", lprefix, lfmt.Format(r)) return nil - })) + }, log.LvlTrace)) // Listen. socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{127, 0, 0, 1}}) diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go index f061f5ab41cc..1cc5fc03e09d 100644 --- a/p2p/discover/v5_udp_test.go +++ b/p2p/discover/v5_udp_test.go @@ -83,7 +83,7 @@ func startLocalhostV5(t *testing.T, cfg Config) *UDPv5 { cfg.Log.SetHandler(log.FuncHandler(func(r *log.Record) error { t.Logf("%s %s", lprefix, lfmt.Format(r)) return nil - })) + }, log.LvlTrace)) // Listen. socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{127, 0, 0, 1}})