mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
Merge pull request #734 from gzliudan/upgrade-log
upgrade package log to 2024-11-04
This commit is contained in:
commit
77b0c0b88f
62 changed files with 1596 additions and 1996 deletions
|
|
@ -64,7 +64,7 @@ func (u URL) String() string {
|
|||
func (u URL) TerminalString() string {
|
||||
url := u.String()
|
||||
if len(url) > 32 {
|
||||
return url[:31] + "…"
|
||||
return url[:31] + ".."
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
|
@ -76,10 +76,9 @@ func (u URL) MarshalJSON() ([]byte, error) {
|
|||
|
||||
// Cmp compares x and y and returns:
|
||||
//
|
||||
// -1 if x < y
|
||||
// 0 if x == y
|
||||
// +1 if x > y
|
||||
//
|
||||
// -1 if x < y
|
||||
// 0 if x == y
|
||||
// +1 if x > y
|
||||
func (u URL) Cmp(url URL) int {
|
||||
if u.Scheme == url.Scheme {
|
||||
return strings.Compare(u.Path, url.Path)
|
||||
|
|
|
|||
|
|
@ -33,8 +33,6 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/cmd/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/eth/ethconfig"
|
||||
"github.com/XinFinOrg/XDPoSChain/internal/debug"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/node"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/naoina/toml"
|
||||
|
|
@ -143,9 +141,9 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) {
|
|||
if ctx.GlobalIsSet(utils.StakingEnabledFlag.Name) {
|
||||
cfg.StakeEnable = ctx.GlobalBool(utils.StakingEnabledFlag.Name)
|
||||
}
|
||||
if !ctx.GlobalIsSet(debug.VerbosityFlag.Name) {
|
||||
debug.Glogger.Verbosity(log.Lvl(cfg.Verbosity))
|
||||
}
|
||||
// if !ctx.GlobalIsSet(debug.VerbosityFlag.Name) {
|
||||
// debug.Verbosity(log.Lvl(cfg.Verbosity))
|
||||
// }
|
||||
|
||||
if !ctx.GlobalIsSet(utils.NATFlag.Name) && cfg.NAT != "" {
|
||||
ctx.Set(utils.NATFlag.Name, cfg.NAT)
|
||||
|
|
|
|||
|
|
@ -136,6 +136,8 @@ var (
|
|||
utils.GpoIgnoreGasPriceFlag,
|
||||
//utils.ExtraDataFlag,
|
||||
configFileFlag,
|
||||
utils.LogDebugFlag,
|
||||
utils.LogBacktraceAtFlag,
|
||||
utils.AnnounceTxsFlag,
|
||||
utils.StoreRewardFlag,
|
||||
utils.RollbackFlag,
|
||||
|
|
|
|||
|
|
@ -51,10 +51,10 @@ func main() {
|
|||
)
|
||||
flag.Parse()
|
||||
|
||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||
glogger.Verbosity(log.Lvl(*verbosity))
|
||||
glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
|
||||
glogger.Verbosity(log.FromLegacyLevel(*verbosity))
|
||||
glogger.Vmodule(*vmodule)
|
||||
log.Root().SetHandler(glogger)
|
||||
log.SetDefault(log.NewLogger(glogger))
|
||||
|
||||
natm, err := nat.Parse(*natdesc)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -110,6 +110,14 @@ var (
|
|||
Name: "nostack",
|
||||
Usage: "disable stack output",
|
||||
}
|
||||
DisableStorageFlag = &cli.BoolFlag{
|
||||
Name: "nostorage",
|
||||
Usage: "disable storage output",
|
||||
}
|
||||
DisableReturnDataFlag = &cli.BoolFlag{
|
||||
Name: "noreturndata",
|
||||
Usage: "enable return data output",
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
|
|
@ -22,21 +22,18 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
goruntime "runtime"
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
|
||||
goruntime "runtime"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/cmd/evm/internal/compiler"
|
||||
"github.com/XinFinOrg/XDPoSChain/cmd/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm/runtime"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
|
@ -71,12 +68,12 @@ func readGenesis(genesisPath string) *core.Genesis {
|
|||
}
|
||||
|
||||
func runCmd(ctx *cli.Context) error {
|
||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
|
||||
log.Root().SetHandler(glogger)
|
||||
logconfig := &vm.LogConfig{
|
||||
EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
|
||||
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
|
||||
EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
|
||||
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
|
||||
DisableStorage: ctx.Bool(DisableStorageFlag.Name),
|
||||
EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
|
||||
Debug: ctx.Bool(DebugFlag.Name),
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -95,6 +92,7 @@ func runCmd(ctx *cli.Context) error {
|
|||
} else {
|
||||
debugLogger = vm.NewStructLogger(logconfig)
|
||||
}
|
||||
|
||||
if ctx.GlobalString(GenesisFlag.Name) != "" {
|
||||
gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
|
|
|
|||
|
|
@ -24,9 +24,7 @@ import (
|
|||
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/tests"
|
||||
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
|
|
@ -49,16 +47,15 @@ func stateTestCmd(ctx *cli.Context) error {
|
|||
if len(ctx.Args().First()) == 0 {
|
||||
return errors.New("path-to-test argument required")
|
||||
}
|
||||
// Configure the go-ethereum logger
|
||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
|
||||
log.Root().SetHandler(glogger)
|
||||
|
||||
// Configure the EVM logger
|
||||
config := &vm.LogConfig{
|
||||
EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
|
||||
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
|
||||
EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
|
||||
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
|
||||
DisableStorage: ctx.Bool(DisableStorageFlag.Name),
|
||||
EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
|
||||
}
|
||||
|
||||
var (
|
||||
tracer vm.EVMLogger
|
||||
debugger *vm.StructLogger
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ var (
|
|||
func main() {
|
||||
// Parse the flags and set up the logger to print everything requested
|
||||
flag.Parse()
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*logFlag), log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.FromLegacyLevel(*logFlag), true)))
|
||||
|
||||
// Construct the payout tiers
|
||||
amounts := make([]string, *tiersFlag)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func main() {
|
|||
}
|
||||
app.Action = func(c *cli.Context) error {
|
||||
// Set up the logger to print everything and the random generator
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
|
||||
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stdout, log.FromLegacyLevel(c.Int("loglevel")), true)))
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
network := c.String("network")
|
||||
|
|
|
|||
|
|
@ -618,6 +618,16 @@ var (
|
|||
Name: "slave",
|
||||
Usage: "Enable slave mode",
|
||||
}
|
||||
// Deprecated November 2023
|
||||
LogBacktraceAtFlag = &cli.StringFlag{
|
||||
Name: "log-backtrace",
|
||||
Usage: "Request a stack trace at a specific logging statement (deprecated)",
|
||||
Value: "",
|
||||
}
|
||||
LogDebugFlag = &cli.BoolFlag{
|
||||
Name: "log-debug",
|
||||
Usage: "Prepends log messages with call-site location (deprecated)",
|
||||
}
|
||||
)
|
||||
|
||||
// MakeDataDir retrieves the currently requested data directory, terminating
|
||||
|
|
@ -1015,6 +1025,13 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
|||
if ctx.GlobalIsSet(AnnounceTxsFlag.Name) {
|
||||
cfg.AnnounceTxs = ctx.GlobalBool(AnnounceTxsFlag.Name)
|
||||
}
|
||||
// deprecation notice for log debug flags (TODO: find a more appropriate place to put these?)
|
||||
if ctx.IsSet(LogBacktraceAtFlag.Name) {
|
||||
log.Warn("log.backtrace flag is deprecated")
|
||||
}
|
||||
if ctx.IsSet(LogDebugFlag.Name) {
|
||||
log.Warn("log.debug flag is deprecated")
|
||||
}
|
||||
}
|
||||
|
||||
func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) {
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ func (h Hash) Cmp(other Hash) int {
|
|||
func (h Hash) IsZero() bool { return h == Hash{} }
|
||||
|
||||
// Get the string representation of the underlying hash
|
||||
func (h Hash) Str() string { return string(h[:]) }
|
||||
func (h Hash) Str() string { return string(h[:]) }
|
||||
|
||||
// Bytes gets the byte representation of the underlying hash.
|
||||
func (h Hash) Bytes() []byte { return h[:] }
|
||||
|
|
@ -116,12 +116,12 @@ func (h Hash) Bytes() []byte { return h[:] }
|
|||
func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) }
|
||||
|
||||
// Hex converts a hash to a hex string.
|
||||
func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
|
||||
func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
|
||||
|
||||
// TerminalString implements log.TerminalStringer, formatting a string for console
|
||||
// output during logging.
|
||||
func (h Hash) TerminalString() string {
|
||||
return fmt.Sprintf("%x…%x", h[:3], h[29:])
|
||||
return fmt.Sprintf("%x..%x", h[:3], h[29:])
|
||||
}
|
||||
|
||||
// String implements the stringer interface and is used also by the logger when
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBytesConversion(t *testing.T) {
|
||||
|
|
@ -203,3 +204,14 @@ func TestStringToBinaryAddress(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrettyDuration(b *testing.B) {
|
||||
var x = PrettyDuration(time.Duration(int64(1203123912312)))
|
||||
b.Logf("Pre %s", time.Duration(x).String())
|
||||
var a string
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
a = x.String()
|
||||
}
|
||||
b.Logf("Post %s", a)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@ var (
|
|||
)
|
||||
|
||||
func TestPriceFeed(t *testing.T) {
|
||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||
glogger.Verbosity(log.LvlTrace)
|
||||
log.Root().SetHandler(glogger)
|
||||
glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
|
||||
glogger.Verbosity(log.LevelTrace)
|
||||
log.SetDefault(log.NewLogger(glogger))
|
||||
|
||||
common.TIPXDCXCancellationFee = big.NewInt(0)
|
||||
// init genesis
|
||||
contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{
|
||||
|
|
|
|||
|
|
@ -440,7 +440,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
|||
// Make sure that both the block as well at its state trie exists
|
||||
block := bc.GetBlockByHash(hash)
|
||||
if block == nil {
|
||||
return fmt.Errorf("non existent block [%x…]", hash[:4])
|
||||
return fmt.Errorf("non existent block [%x..]", hash[:4])
|
||||
}
|
||||
if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB()); err != nil {
|
||||
return err
|
||||
|
|
@ -1055,7 +1055,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() {
|
||||
log.Error("Non contiguous receipt insert", "number", blockChain[i].Number(), "hash", blockChain[i].Hash(), "parent", blockChain[i].ParentHash(),
|
||||
"prevnumber", blockChain[i-1].Number(), "prevhash", blockChain[i-1].Hash())
|
||||
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, blockChain[i-1].NumberU64(),
|
||||
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, blockChain[i-1].NumberU64(),
|
||||
blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4])
|
||||
}
|
||||
}
|
||||
|
|
@ -1075,7 +1075,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
blockHash, blockNumber := block.Hash(), block.NumberU64()
|
||||
// Short circuit if the owner header is unknown
|
||||
if !bc.HasHeader(blockHash, blockNumber) {
|
||||
return i, fmt.Errorf("containing header #%d [%x…] unknown", blockNumber, blockHash.Bytes()[:4])
|
||||
return i, fmt.Errorf("containing header #%d [%x..] unknown", blockNumber, blockHash.Bytes()[:4])
|
||||
}
|
||||
// Skip if the entire data is already known
|
||||
if bc.HasBlock(blockHash, blockNumber) {
|
||||
|
|
@ -1422,7 +1422,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
|||
log.Error("Non contiguous block insert", "number", chain[i].Number(), "hash", chain[i].Hash(),
|
||||
"parent", chain[i].ParentHash(), "prevnumber", chain[i-1].Number(), "prevhash", chain[i-1].Hash())
|
||||
|
||||
return 0, nil, nil, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].NumberU64(),
|
||||
return 0, nil, nil, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, chain[i-1].NumberU64(),
|
||||
chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1221,13 +1221,13 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
|||
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
|
||||
}
|
||||
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
|
||||
t.Errorf("block %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
|
||||
t.Errorf("block %d: current block/header mismatch: block #%d [%x..], header #%d [%x..]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
|
||||
}
|
||||
if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
|
||||
t.Fatalf(" fork %d: failed to insert into chain: %v", i, err)
|
||||
}
|
||||
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
|
||||
t.Errorf(" fork %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
|
||||
t.Errorf(" fork %d: current block/header mismatch: block #%d [%x..], header #%d [%x..]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -356,7 +356,7 @@ func (c *ChainIndexer) processSection(section uint64, lastHead common.Hash) (com
|
|||
}
|
||||
header := GetHeader(c.chainDb, hash, number)
|
||||
if header == nil {
|
||||
return common.Hash{}, fmt.Errorf("block #%d [%x…] not found", number, hash[:4])
|
||||
return common.Hash{}, fmt.Errorf("block #%d [%x..] not found", number, hash[:4])
|
||||
} else if header.ParentHash != lastHead {
|
||||
return common.Hash{}, errors.New("chain reorged during section processing")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int)
|
|||
log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", chain[i].Hash(),
|
||||
"parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", chain[i-1].Hash())
|
||||
|
||||
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].Number,
|
||||
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, chain[i-1].Number,
|
||||
chain[i-1].Hash().Bytes()[:4], i, chain[i].Number, chain[i].Hash().Bytes()[:4], chain[i].ParentHash[:4])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import (
|
|||
)
|
||||
|
||||
func init() {
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, false)))
|
||||
}
|
||||
|
||||
var testAccount, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
|
|
|
|||
27
go.mod
27
go.mod
|
|
@ -3,7 +3,6 @@ module github.com/XinFinOrg/XDPoSChain
|
|||
go 1.21
|
||||
|
||||
require (
|
||||
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669
|
||||
github.com/VictoriaMetrics/fastcache v1.12.2
|
||||
github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98
|
||||
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6
|
||||
|
|
@ -12,9 +11,7 @@ require (
|
|||
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf
|
||||
github.com/edsrzf/mmap-go v1.0.0
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/gizak/termui v2.2.0+incompatible
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
|
||||
github.com/go-stack/stack v1.8.1
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
|
|
@ -38,7 +35,6 @@ require (
|
|||
github.com/stretchr/testify v1.8.4
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
|
||||
golang.org/x/crypto v0.15.0
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/sync v0.4.0
|
||||
golang.org/x/sys v0.24.0
|
||||
golang.org/x/tools v0.14.0
|
||||
|
|
@ -48,37 +44,32 @@ require (
|
|||
gopkg.in/urfave/cli.v1 v1.20.0
|
||||
)
|
||||
|
||||
require github.com/deckarep/golang-set v1.8.0
|
||||
require (
|
||||
github.com/deckarep/golang-set v1.8.0
|
||||
github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/mattn/go-isatty v0.0.17
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/StackExchange/wmi v1.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||
github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29 // indirect
|
||||
github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa // indirect
|
||||
github.com/go-ole/go-ole v1.2.5 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect
|
||||
github.com/maruel/ut v1.0.2 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
|
||||
github.com/naoina/go-stringutil v0.1.0 // indirect
|
||||
github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
|
||||
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/term v0.14.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
|
|
|
|||
176
go.sum
176
go.sum
|
|
@ -1,113 +1,44 @@
|
|||
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669 h1:FNCRpXiquG1aoyqcIWVFmpTSKVcx2bQD38uZZeGtdlw=
|
||||
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
||||
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
|
||||
github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
|
||||
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.3/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE=
|
||||
github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40=
|
||||
github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o=
|
||||
github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
|
||||
github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
|
||||
github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98 h1:7buXGE+m4OPjyo8rUJgA8RmARNMq+m99JJLR+Z+ZWN0=
|
||||
github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98/go.mod h1:DLTg9Gp4FAXF5EpqYBQnUeBbRsNLY7b2HR94TE5XQtE=
|
||||
github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI=
|
||||
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
|
||||
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||
github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
|
||||
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.0.1-0.20190104013014-3767db7a7e18/go.mod h1:HD5P3vAIAh+Y2GAxg0PrPN1P8WkepXGpjbUPDHJqqKM=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
||||
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
|
||||
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
|
||||
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M=
|
||||
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29 h1:Ewd9K+mC725sITA12QQHRqWj78NU4t7EhlFVVgdlzJg=
|
||||
github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
|
||||
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5RVoRccG8a5DhOdWdQ4zN62zzo=
|
||||
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa h1:XKAhUk/dtp+CV0VO6mhG2V7jA9vbcGcnYF/Ay9NjZrY=
|
||||
github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs=
|
||||
github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk=
|
||||
github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0=
|
||||
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
||||
github.com/gizak/termui v2.2.0+incompatible h1:qvZU9Xll/Xd/Xr/YO+HfBKXhy8a8/94ao6vV9DSXzUE=
|
||||
github.com/gizak/termui v2.2.0+incompatible/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
||||
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
|
|
@ -117,7 +48,6 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
|||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
|
||||
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
|
|
@ -125,48 +55,29 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
|
||||
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o=
|
||||
github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
|
||||
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
|
||||
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag=
|
||||
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
|
||||
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
|
||||
github.com/influxdata/influxdb v1.7.9 h1:uSeBTNO4rBkbp1Be5FKRsAmglM9nlx25TzVQRQt1An4=
|
||||
github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/karalabe/hid v1.0.0 h1:+/CIMNXhSU/zIJgnIvBD2nKHxS/bnRHhhs9xBryLpPo=
|
||||
github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8=
|
||||
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
|
|
@ -175,145 +86,86 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e h1:e2z/lz9pvtRrEOgKWaLW2Dw02Nqd3/fqv0qWTQ8ByZE=
|
||||
github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e/go.mod h1:nty42YY5QByNC5MM7q/nj938VbgPU7avs45z6NClpxI=
|
||||
github.com/maruel/ut v1.0.2 h1:mQTlQk3jubTbdTcza+hwoZQWhzcvE4L6K6RTtAFlA1k=
|
||||
github.com/maruel/ut v1.0.2/go.mod h1:RV8PwPD9dd2KFlnlCc/DB2JVvkXmyaalfc5xvmSrRSs=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
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-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
|
||||
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
|
||||
github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
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.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=
|
||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0=
|
||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77 h1:gKl78uP/I7JZ56OFtRf7nc4m1icV38hwV0In5pEGzeA=
|
||||
github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
|
||||
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5 h1:K2PKeDFZidfjUWpXk05Gbxhwm8Rnz1l4O+u/bbbcCvc=
|
||||
github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s=
|
||||
github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
|
||||
github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8=
|
||||
github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
|
||||
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
|
||||
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw=
|
||||
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM=
|
||||
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -323,39 +175,21 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
|
||||
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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=
|
||||
|
|
@ -369,24 +203,20 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
|||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 h1:hhsSf/5z74Ck/DJYc+R8zpq8KGm7uJvpdLRQED/IedA=
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
||||
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ package debug
|
|||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
|
|
@ -55,19 +56,13 @@ type HandlerT struct {
|
|||
// Verbosity sets the log verbosity ceiling. The verbosity of individual packages
|
||||
// and source files can be raised using Vmodule.
|
||||
func (*HandlerT) Verbosity(level int) {
|
||||
Glogger.Verbosity(log.Lvl(level))
|
||||
glogger.Verbosity(slog.Level(level))
|
||||
}
|
||||
|
||||
// Vmodule sets the log verbosity pattern. See package log for details on the
|
||||
// pattern syntax.
|
||||
func (*HandlerT) Vmodule(pattern string) error {
|
||||
return Glogger.Vmodule(pattern)
|
||||
}
|
||||
|
||||
// BacktraceAt sets the log backtrace location. See package log for details on
|
||||
// the pattern syntax.
|
||||
func (*HandlerT) BacktraceAt(location string) error {
|
||||
return Glogger.BacktraceAt(location)
|
||||
return glogger.Vmodule(pattern)
|
||||
}
|
||||
|
||||
// MemStats returns detailed runtime memory statistics.
|
||||
|
|
|
|||
|
|
@ -19,38 +19,73 @@ package debug
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/log/term"
|
||||
"github.com/XinFinOrg/XDPoSChain/metrics"
|
||||
"github.com/XinFinOrg/XDPoSChain/metrics/exp"
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
VerbosityFlag = cli.IntFlag{
|
||||
verbosityFlag = cli.IntFlag{
|
||||
Name: "verbosity",
|
||||
Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail",
|
||||
Value: 3,
|
||||
}
|
||||
logVmoduleFlag = &cli.StringFlag{
|
||||
Name: "log-vmodule",
|
||||
Usage: "Per-module verbosity: comma-separated list of <pattern>=<level> (e.g. eth/*=5,p2p=4)",
|
||||
Value: "",
|
||||
}
|
||||
vmoduleFlag = cli.StringFlag{
|
||||
Name: "vmodule",
|
||||
Usage: "Per-module verbosity: comma-separated list of <pattern>=<level> (e.g. eth/*=5,p2p=4)",
|
||||
Value: "",
|
||||
}
|
||||
backtraceAtFlag = cli.StringFlag{
|
||||
Name: "backtrace",
|
||||
Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")",
|
||||
Value: "",
|
||||
logjsonFlag = &cli.BoolFlag{
|
||||
Name: "log-json",
|
||||
Usage: "Format logs with JSON",
|
||||
Hidden: true,
|
||||
}
|
||||
debugFlag = cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Usage: "Prepends log messages with call-site location (file and line number)",
|
||||
logFormatFlag = &cli.StringFlag{
|
||||
Name: "log-format",
|
||||
Usage: "Log format to use (json|logfmt|terminal)",
|
||||
}
|
||||
logFileFlag = &cli.StringFlag{
|
||||
Name: "log-file",
|
||||
Usage: "Write logs to a file",
|
||||
}
|
||||
logRotateFlag = &cli.BoolFlag{
|
||||
Name: "log-rotate",
|
||||
Usage: "Enables log file rotation",
|
||||
}
|
||||
logMaxSizeMBsFlag = &cli.IntFlag{
|
||||
Name: "log-maxsize",
|
||||
Usage: "Maximum size in MBs of a single log file",
|
||||
Value: 100,
|
||||
}
|
||||
logMaxBackupsFlag = &cli.IntFlag{
|
||||
Name: "log-maxbackups",
|
||||
Usage: "Maximum number of log files to retain",
|
||||
Value: 10,
|
||||
}
|
||||
logMaxAgeFlag = &cli.IntFlag{
|
||||
Name: "log-maxage",
|
||||
Usage: "Maximum number of days to retain a log file",
|
||||
Value: 30,
|
||||
}
|
||||
logCompressFlag = &cli.BoolFlag{
|
||||
Name: "log-compress",
|
||||
Usage: "Compress the log files",
|
||||
}
|
||||
pprofFlag = cli.BoolFlag{
|
||||
Name: "pprof",
|
||||
|
|
@ -95,10 +130,17 @@ var (
|
|||
|
||||
// Flags holds all command-line flags required for debugging.
|
||||
var Flags = []cli.Flag{
|
||||
VerbosityFlag,
|
||||
//vmoduleFlag,
|
||||
//backtraceAtFlag,
|
||||
debugFlag,
|
||||
verbosityFlag,
|
||||
logVmoduleFlag,
|
||||
vmoduleFlag,
|
||||
logjsonFlag,
|
||||
logFormatFlag,
|
||||
logFileFlag,
|
||||
logRotateFlag,
|
||||
logMaxSizeMBsFlag,
|
||||
logMaxBackupsFlag,
|
||||
logMaxAgeFlag,
|
||||
logCompressFlag,
|
||||
pprofFlag,
|
||||
pprofAddrFlag,
|
||||
pprofPortFlag,
|
||||
|
|
@ -110,26 +152,117 @@ var Flags = []cli.Flag{
|
|||
debugDataDirFlag,
|
||||
}
|
||||
|
||||
var Glogger *log.GlogHandler
|
||||
var (
|
||||
glogger *log.GlogHandler
|
||||
logOutputFile io.WriteCloser
|
||||
defaultTerminalHandler *log.TerminalHandler
|
||||
)
|
||||
|
||||
func init() {
|
||||
usecolor := term.IsTty(os.Stderr.Fd()) && os.Getenv("TERM") != "dumb"
|
||||
output := io.Writer(os.Stderr)
|
||||
if usecolor {
|
||||
output = colorable.NewColorableStderr()
|
||||
defaultTerminalHandler = log.NewTerminalHandler(os.Stderr, false)
|
||||
glogger = log.NewGlogHandler(defaultTerminalHandler)
|
||||
glogger.Verbosity(log.LvlInfo)
|
||||
log.SetDefault(log.NewLogger(glogger))
|
||||
}
|
||||
|
||||
func ResetLogging() {
|
||||
if defaultTerminalHandler != nil {
|
||||
defaultTerminalHandler.ResetFieldPadding()
|
||||
}
|
||||
Glogger = log.NewGlogHandler(log.StreamHandler(output, log.TerminalFormat(usecolor)))
|
||||
}
|
||||
|
||||
// Setup initializes profiling and logging based on the CLI flags.
|
||||
// It should be called as early as possible in the program.
|
||||
func Setup(ctx *cli.Context) error {
|
||||
var (
|
||||
handler slog.Handler
|
||||
terminalOutput = io.Writer(os.Stderr)
|
||||
output io.Writer
|
||||
logFmtFlag = ctx.String(logFormatFlag.Name)
|
||||
)
|
||||
var (
|
||||
logFile = ctx.String(logFileFlag.Name)
|
||||
rotation = ctx.Bool(logRotateFlag.Name)
|
||||
)
|
||||
if len(logFile) > 0 {
|
||||
if err := validateLogLocation(filepath.Dir(logFile)); err != nil {
|
||||
return fmt.Errorf("failed to initiatilize file logger: %v", err)
|
||||
}
|
||||
}
|
||||
context := []interface{}{"rotate", rotation}
|
||||
if len(logFmtFlag) > 0 {
|
||||
context = append(context, "format", logFmtFlag)
|
||||
} else {
|
||||
context = append(context, "format", "terminal")
|
||||
}
|
||||
if rotation {
|
||||
// Lumberjack uses <processname>-lumberjack.log in is.TempDir() if empty.
|
||||
// so typically /tmp/geth-lumberjack.log on linux
|
||||
if len(logFile) > 0 {
|
||||
context = append(context, "location", logFile)
|
||||
} else {
|
||||
context = append(context, "location", filepath.Join(os.TempDir(), "geth-lumberjack.log"))
|
||||
}
|
||||
logOutputFile = &lumberjack.Logger{
|
||||
Filename: logFile,
|
||||
MaxSize: ctx.Int(logMaxSizeMBsFlag.Name),
|
||||
MaxBackups: ctx.Int(logMaxBackupsFlag.Name),
|
||||
MaxAge: ctx.Int(logMaxAgeFlag.Name),
|
||||
Compress: ctx.Bool(logCompressFlag.Name),
|
||||
}
|
||||
output = io.MultiWriter(terminalOutput, logOutputFile)
|
||||
} else if logFile != "" {
|
||||
var err error
|
||||
if logOutputFile, err = os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
output = io.MultiWriter(logOutputFile, terminalOutput)
|
||||
context = append(context, "location", logFile)
|
||||
} else {
|
||||
output = terminalOutput
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.Bool(logjsonFlag.Name):
|
||||
// Retain backwards compatibility with `--log-json` flag if `--log-format` not set
|
||||
defer log.Warn("The flag '--log-json' is deprecated, please use '--log-format=json' instead")
|
||||
handler = log.JSONHandlerWithLevel(output, log.LevelInfo)
|
||||
case logFmtFlag == "json":
|
||||
handler = log.JSONHandlerWithLevel(output, log.LevelInfo)
|
||||
case logFmtFlag == "logfmt":
|
||||
handler = log.LogfmtHandler(output)
|
||||
case logFmtFlag == "", logFmtFlag == "terminal":
|
||||
useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
|
||||
if useColor {
|
||||
terminalOutput = colorable.NewColorableStderr()
|
||||
if logOutputFile != nil {
|
||||
output = io.MultiWriter(logOutputFile, terminalOutput)
|
||||
} else {
|
||||
output = terminalOutput
|
||||
}
|
||||
}
|
||||
handler = log.NewTerminalHandler(output, useColor)
|
||||
default:
|
||||
// Unknown log format specified
|
||||
return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name))
|
||||
}
|
||||
|
||||
glogger = log.NewGlogHandler(handler)
|
||||
|
||||
// logging
|
||||
log.PrintOrigins(ctx.GlobalBool(debugFlag.Name))
|
||||
Glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
|
||||
Glogger.Vmodule(ctx.GlobalString(vmoduleFlag.Name))
|
||||
Glogger.BacktraceAt(ctx.GlobalString(backtraceAtFlag.Name))
|
||||
log.Root().SetHandler(Glogger)
|
||||
verbosity := log.FromLegacyLevel(ctx.Int(verbosityFlag.Name))
|
||||
glogger.Verbosity(verbosity)
|
||||
vmodule := ctx.String(logVmoduleFlag.Name)
|
||||
if vmodule == "" {
|
||||
// Retain backwards compatibility with `--vmodule` flag if `--log-vmodule` not set
|
||||
vmodule = ctx.String(vmoduleFlag.Name)
|
||||
if vmodule != "" {
|
||||
defer log.Warn("The flag '--vmodule' is deprecated, please use '--log-vmodule' instead")
|
||||
}
|
||||
}
|
||||
glogger.Vmodule(vmodule)
|
||||
|
||||
log.SetDefault(log.NewLogger(glogger))
|
||||
|
||||
// profiling, tracing
|
||||
runtime.MemProfileRate = ctx.GlobalInt(memprofilerateFlag.Name)
|
||||
|
|
@ -164,6 +297,11 @@ func Setup(ctx *cli.Context) error {
|
|||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if len(logFile) > 0 || rotation {
|
||||
log.Info("Logging configured", context...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -172,4 +310,21 @@ func Setup(ctx *cli.Context) error {
|
|||
func Exit() {
|
||||
Handler.StopCPUProfile()
|
||||
Handler.StopGoTrace()
|
||||
if logOutputFile != nil {
|
||||
logOutputFile.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func validateLogLocation(path string) error {
|
||||
if err := os.MkdirAll(path, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("error creating the directory: %w", err)
|
||||
}
|
||||
// Check if the path is writable by trying to create a temporary file
|
||||
tmp := filepath.Join(path, "tmp")
|
||||
if f, err := os.Create(tmp); err != nil {
|
||||
return err
|
||||
} else {
|
||||
f.Close()
|
||||
}
|
||||
return os.Remove(tmp)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// +build go1.6
|
||||
|
||||
package debug
|
||||
|
||||
import "runtime/debug"
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// +build !go1.6
|
||||
|
||||
package debug
|
||||
|
||||
// LoudPanic panics in a way that gets all goroutine stacks printed on stderr.
|
||||
func LoudPanic(x interface{}) {
|
||||
panic(x)
|
||||
}
|
||||
|
|
@ -14,8 +14,6 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//+build go1.5
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//+build !go1.5
|
||||
|
||||
// no-op implementation of tracing methods for Go < 1.5.
|
||||
|
||||
package debug
|
||||
|
||||
import "errors"
|
||||
|
||||
func (*HandlerT) StartGoTrace(string) error {
|
||||
return errors.New("tracing is not supported on Go < 1.5")
|
||||
}
|
||||
|
||||
func (*HandlerT) StopGoTrace() error {
|
||||
return errors.New("tracing is not supported on Go < 1.5")
|
||||
}
|
||||
207
internal/testlog/testlog.go
Normal file
207
internal/testlog/testlog.go
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package testlog provides a log handler for unit tests.
|
||||
package testlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
)
|
||||
|
||||
const (
|
||||
termTimeFormat = "01-02|15:04:05.000"
|
||||
)
|
||||
|
||||
// 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
|
||||
// which emitted the log message.
|
||||
type logger struct {
|
||||
t *testing.T
|
||||
l log.Logger
|
||||
mu *sync.Mutex
|
||||
h *bufHandler
|
||||
}
|
||||
|
||||
type bufHandler struct {
|
||||
buf []slog.Record
|
||||
attrs []slog.Attr
|
||||
level slog.Level
|
||||
}
|
||||
|
||||
func (h *bufHandler) Handle(_ context.Context, r slog.Record) error {
|
||||
h.buf = append(h.buf, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *bufHandler) Enabled(_ context.Context, lvl slog.Level) bool {
|
||||
return lvl <= h.level
|
||||
}
|
||||
|
||||
func (h *bufHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
records := make([]slog.Record, len(h.buf))
|
||||
copy(records[:], h.buf[:])
|
||||
return &bufHandler{
|
||||
records,
|
||||
append(h.attrs, attrs...),
|
||||
h.level,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *bufHandler) WithGroup(_ string) slog.Handler {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// Logger returns a logger which logs to the unit test log of t.
|
||||
func Logger(t *testing.T, level slog.Level) log.Logger {
|
||||
handler := bufHandler{
|
||||
[]slog.Record{},
|
||||
[]slog.Attr{},
|
||||
level,
|
||||
}
|
||||
return &logger{
|
||||
t: t,
|
||||
l: log.NewLogger(&handler),
|
||||
mu: new(sync.Mutex),
|
||||
h: &handler,
|
||||
}
|
||||
}
|
||||
|
||||
// LoggerWithHandler returns
|
||||
func LoggerWithHandler(t *testing.T, handler slog.Handler) log.Logger {
|
||||
var bh bufHandler
|
||||
return &logger{
|
||||
t: t,
|
||||
l: log.NewLogger(handler),
|
||||
mu: new(sync.Mutex),
|
||||
h: &bh,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logger) Handler() slog.Handler {
|
||||
return l.l.Handler()
|
||||
}
|
||||
|
||||
func (l *logger) Write(level slog.Level, msg string, ctx ...interface{}) {}
|
||||
|
||||
func (l *logger) Enabled(ctx context.Context, level slog.Level) bool {
|
||||
return l.l.Enabled(ctx, level)
|
||||
}
|
||||
|
||||
func (l *logger) Trace(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Trace(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Log(level slog.Level, msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Log(level, msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Debug(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Debug(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Info(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Info(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Warn(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Warn(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Error(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Error(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Crit(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Crit(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) With(ctx ...interface{}) log.Logger {
|
||||
return &logger{l.t, l.l.With(ctx...), l.mu, l.h}
|
||||
}
|
||||
|
||||
func (l *logger) New(ctx ...interface{}) log.Logger {
|
||||
return l.With(ctx...)
|
||||
}
|
||||
|
||||
// terminalFormat formats a message similarly to the NewTerminalHandler in the log package.
|
||||
// The difference is that terminalFormat does not escape messages/attributes and does not pad attributes.
|
||||
func (h *bufHandler) terminalFormat(r slog.Record) string {
|
||||
buf := &bytes.Buffer{}
|
||||
lvl := log.LevelAlignedString(r.Level)
|
||||
attrs := []slog.Attr{}
|
||||
r.Attrs(func(attr slog.Attr) bool {
|
||||
attrs = append(attrs, attr)
|
||||
return true
|
||||
})
|
||||
|
||||
attrs = append(h.attrs, attrs...)
|
||||
|
||||
fmt.Fprintf(buf, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Message)
|
||||
if length := len(r.Message); length < 40 {
|
||||
buf.Write(bytes.Repeat([]byte{' '}, 40-length))
|
||||
}
|
||||
|
||||
for _, attr := range attrs {
|
||||
fmt.Fprintf(buf, " %s=%s", attr.Key, string(log.FormatSlogValue(attr.Value, nil)))
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// flush writes all buffered messages and clears the buffer.
|
||||
func (l *logger) flush() {
|
||||
l.t.Helper()
|
||||
for _, r := range l.h.buf {
|
||||
l.t.Logf("%s", l.h.terminalFormat(r))
|
||||
}
|
||||
l.h.buf = nil
|
||||
}
|
||||
|
|
@ -450,7 +450,10 @@ func (p *TxPool) add(ctx context.Context, tx *types.Transaction) error {
|
|||
}
|
||||
|
||||
// Print a log message if low enough level is set
|
||||
log.Debug("Pooled new transaction", "hash", hash, "from", log.Lazy{Fn: func() common.Address { from, _ := types.Sender(p.signer, tx); return from }}, "to", tx.To())
|
||||
if log.Enabled(log.LevelDebug) {
|
||||
from, _ := types.Sender(p.signer, tx)
|
||||
log.Debug("Pooled new transaction", "hash", hash, "from", from, "to", tx.To())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
Contributors to log15:
|
||||
|
||||
- Aaron L
|
||||
- Alan Shreve
|
||||
- Chris Hines
|
||||
- Ciaran Downey
|
||||
- Dmitry Chestnykh
|
||||
- Evan Shaw
|
||||
- Péter Szilágyi
|
||||
- Trevor Gattis
|
||||
- Vincent Vanackere
|
||||
13
log/LICENSE
13
log/LICENSE
|
|
@ -1,13 +0,0 @@
|
|||
Copyright 2014 Alan Shreve
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -1,77 +0,0 @@
|
|||

|
||||
|
||||
# log15 [](https://godoc.org/github.com/inconshreveable/log15) [](https://travis-ci.org/inconshreveable/log15)
|
||||
|
||||
Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](http://golang.org/pkg/io/) and [`net/http`](http://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](http://golang.org/pkg/log/) package.
|
||||
|
||||
## Features
|
||||
- A simple, easy-to-understand API
|
||||
- Promotes structured logging by encouraging use of key/value pairs
|
||||
- Child loggers which inherit and add their own private context
|
||||
- Lazy evaluation of expensive operations
|
||||
- Simple Handler interface allowing for construction of flexible, custom logging configurations with a tiny API.
|
||||
- Color terminal support
|
||||
- Built-in support for logging to files, streams, syslog, and the network
|
||||
- Support for forking records to multiple handlers, buffering records for output, failing over from failed handler writes, + more
|
||||
|
||||
## Versioning
|
||||
The API of the master branch of log15 should always be considered unstable. If you want to rely on a stable API,
|
||||
you must vendor the library.
|
||||
|
||||
## Importing
|
||||
|
||||
```go
|
||||
import log "github.com/inconshreveable/log15"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```go
|
||||
// all loggers can have key/value context
|
||||
srvlog := log.New("module", "app/server")
|
||||
|
||||
// all log messages can have key/value context
|
||||
srvlog.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate)
|
||||
|
||||
// child loggers with inherited context
|
||||
connlog := srvlog.New("raddr", c.RemoteAddr())
|
||||
connlog.Info("connection open")
|
||||
|
||||
// lazy evaluation
|
||||
connlog.Debug("ping remote", "latency", log.Lazy{pingRemote})
|
||||
|
||||
// flexible configuration
|
||||
srvlog.SetHandler(log.MultiHandler(
|
||||
log.StreamHandler(os.Stderr, log.LogfmtFormat()),
|
||||
log.LvlFilterHandler(
|
||||
log.LvlError,
|
||||
log.Must.FileHandler("errors.json", log.JsonFormat()))))
|
||||
```
|
||||
|
||||
Will result in output that looks like this:
|
||||
|
||||
```
|
||||
WARN[06-17|21:58:10] abnormal conn rate module=app/server rate=0.500 low=0.100 high=0.800
|
||||
INFO[06-17|21:58:10] connection open module=app/server raddr=10.0.0.1
|
||||
```
|
||||
|
||||
## Breaking API Changes
|
||||
The following commits broke API stability. This reference is intended to help you understand the consequences of updating to a newer version
|
||||
of log15.
|
||||
|
||||
- 57a084d014d4150152b19e4e531399a7145d1540 - Added a `Get()` method to the `Logger` interface to retrieve the current handler
|
||||
- 93404652ee366648fa622b64d1e2b67d75a3094a - `Record` field `Call` changed to `stack.Call` with switch to `github.com/go-stack/stack`
|
||||
- a5e7613673c73281f58e15a87d2cf0cf111e8152 - Restored `syslog.Priority` argument to the `SyslogXxx` handler constructors
|
||||
|
||||
## FAQ
|
||||
|
||||
### The varargs style is brittle and error prone! Can I have type safety please?
|
||||
Yes. Use `log.Ctx`:
|
||||
|
||||
```go
|
||||
srvlog := log.New(log.Ctx{"module": "app/server"})
|
||||
srvlog.Warn("abnormal conn rate", log.Ctx{"rate": curRate, "low": lowRate, "high": highRate})
|
||||
```
|
||||
|
||||
## License
|
||||
Apache
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
This package is a fork of https://github.com/inconshreveable/log15, with some
|
||||
minor modifications required by the go-ethereum codebase:
|
||||
|
||||
* Support for log level `trace`
|
||||
* Modified behavior to exit on `critical` failure
|
||||
333
log/doc.go
333
log/doc.go
|
|
@ -1,333 +0,0 @@
|
|||
/*
|
||||
Package log15 provides an opinionated, simple toolkit for best-practice logging that is
|
||||
both human and machine readable. It is modeled after the standard library's io and net/http
|
||||
packages.
|
||||
|
||||
This package enforces you to only log key/value pairs. Keys must be strings. Values may be
|
||||
any type that you like. The default output format is logfmt, but you may also choose to use
|
||||
JSON instead if that suits you. Here's how you log:
|
||||
|
||||
log.Info("page accessed", "path", r.URL.Path, "user_id", user.id)
|
||||
|
||||
This will output a line that looks like:
|
||||
|
||||
lvl=info t=2014-05-02T16:07:23-0700 msg="page accessed" path=/org/71/profile user_id=9
|
||||
|
||||
Getting Started
|
||||
|
||||
To get started, you'll want to import the library:
|
||||
|
||||
import log "github.com/inconshreveable/log15"
|
||||
|
||||
|
||||
Now you're ready to start logging:
|
||||
|
||||
func main() {
|
||||
log.Info("Program starting", "args", os.Args())
|
||||
}
|
||||
|
||||
|
||||
Convention
|
||||
|
||||
Because recording a human-meaningful message is common and good practice, the first argument to every
|
||||
logging method is the value to the *implicit* key 'msg'.
|
||||
|
||||
Additionally, the level you choose for a message will be automatically added with the key 'lvl', and so
|
||||
will the current timestamp with key 't'.
|
||||
|
||||
You may supply any additional context as a set of key/value pairs to the logging function. log15 allows
|
||||
you to favor terseness, ordering, and speed over safety. This is a reasonable tradeoff for
|
||||
logging functions. You don't need to explicitly state keys/values, log15 understands that they alternate
|
||||
in the variadic argument list:
|
||||
|
||||
log.Warn("size out of bounds", "low", lowBound, "high", highBound, "val", val)
|
||||
|
||||
If you really do favor your type-safety, you may choose to pass a log.Ctx instead:
|
||||
|
||||
log.Warn("size out of bounds", log.Ctx{"low": lowBound, "high": highBound, "val": val})
|
||||
|
||||
|
||||
Context loggers
|
||||
|
||||
Frequently, you want to add context to a logger so that you can track actions associated with it. An http
|
||||
request is a good example. You can easily create new loggers that have context that is automatically included
|
||||
with each log line:
|
||||
|
||||
requestlogger := log.New("path", r.URL.Path)
|
||||
|
||||
// later
|
||||
requestlogger.Debug("db txn commit", "duration", txnTimer.Finish())
|
||||
|
||||
This will output a log line that includes the path context that is attached to the logger:
|
||||
|
||||
lvl=dbug t=2014-05-02T16:07:23-0700 path=/repo/12/add_hook msg="db txn commit" duration=0.12
|
||||
|
||||
|
||||
Handlers
|
||||
|
||||
The Handler interface defines where log lines are printed to and how they are formated. Handler is a
|
||||
single interface that is inspired by net/http's handler interface:
|
||||
|
||||
type Handler interface {
|
||||
Log(r *Record) error
|
||||
}
|
||||
|
||||
|
||||
Handlers can filter records, format them, or dispatch to multiple other Handlers.
|
||||
This package implements a number of Handlers for common logging patterns that are
|
||||
easily composed to create flexible, custom logging structures.
|
||||
|
||||
Here's an example handler that prints logfmt output to Stdout:
|
||||
|
||||
handler := log.StreamHandler(os.Stdout, log.LogfmtFormat())
|
||||
|
||||
Here's an example handler that defers to two other handlers. One handler only prints records
|
||||
from the rpc package in logfmt to standard out. The other prints records at Error level
|
||||
or above in JSON formatted output to the file /var/log/service.json
|
||||
|
||||
handler := log.MultiHandler(
|
||||
log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JsonFormat())),
|
||||
log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler())
|
||||
)
|
||||
|
||||
Logging File Names and Line Numbers
|
||||
|
||||
This package implements three Handlers that add debugging information to the
|
||||
context, CallerFileHandler, CallerFuncHandler and CallerStackHandler. Here's
|
||||
an example that adds the source file and line number of each logging call to
|
||||
the context.
|
||||
|
||||
h := log.CallerFileHandler(log.StdoutHandler)
|
||||
log.Root().SetHandler(h)
|
||||
...
|
||||
log.Error("open file", "err", err)
|
||||
|
||||
This will output a line that looks like:
|
||||
|
||||
lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" caller=data.go:42
|
||||
|
||||
Here's an example that logs the call stack rather than just the call site.
|
||||
|
||||
h := log.CallerStackHandler("%+v", log.StdoutHandler)
|
||||
log.Root().SetHandler(h)
|
||||
...
|
||||
log.Error("open file", "err", err)
|
||||
|
||||
This will output a line that looks like:
|
||||
|
||||
lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" stack="[pkg/data.go:42 pkg/cmd/main.go]"
|
||||
|
||||
The "%+v" format instructs the handler to include the path of the source file
|
||||
relative to the compile time GOPATH. The github.com/go-stack/stack package
|
||||
documents the full list of formatting verbs and modifiers available.
|
||||
|
||||
Custom Handlers
|
||||
|
||||
The Handler interface is so simple that it's also trivial to write your own. Let's create an
|
||||
example handler which tries to write to one handler, but if that fails it falls back to
|
||||
writing to another handler and includes the error that it encountered when trying to write
|
||||
to the primary. This might be useful when trying to log over a network socket, but if that
|
||||
fails you want to log those records to a file on disk.
|
||||
|
||||
type BackupHandler struct {
|
||||
Primary Handler
|
||||
Secondary Handler
|
||||
}
|
||||
|
||||
func (h *BackupHandler) Log (r *Record) error {
|
||||
err := h.Primary.Log(r)
|
||||
if err != nil {
|
||||
r.Ctx = append(ctx, "primary_err", err)
|
||||
return h.Secondary.Log(r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
This pattern is so useful that a generic version that handles an arbitrary number of Handlers
|
||||
is included as part of this library called FailoverHandler.
|
||||
|
||||
Logging Expensive Operations
|
||||
|
||||
Sometimes, you want to log values that are extremely expensive to compute, but you don't want to pay
|
||||
the price of computing them if you haven't turned up your logging level to a high level of detail.
|
||||
|
||||
This package provides a simple type to annotate a logging operation that you want to be evaluated
|
||||
lazily, just when it is about to be logged, so that it would not be evaluated if an upstream Handler
|
||||
filters it out. Just wrap any function which takes no arguments with the log.Lazy type. For example:
|
||||
|
||||
func factorRSAKey() (factors []int) {
|
||||
// return the factors of a very large number
|
||||
}
|
||||
|
||||
log.Debug("factors", log.Lazy{factorRSAKey})
|
||||
|
||||
If this message is not logged for any reason (like logging at the Error level), then
|
||||
factorRSAKey is never evaluated.
|
||||
|
||||
Dynamic context values
|
||||
|
||||
The same log.Lazy mechanism can be used to attach context to a logger which you want to be
|
||||
evaluated when the message is logged, but not when the logger is created. For example, let's imagine
|
||||
a game where you have Player objects:
|
||||
|
||||
type Player struct {
|
||||
name string
|
||||
alive bool
|
||||
log.Logger
|
||||
}
|
||||
|
||||
You always want to log a player's name and whether they're alive or dead, so when you create the player
|
||||
object, you might do:
|
||||
|
||||
p := &Player{name: name, alive: true}
|
||||
p.Logger = log.New("name", p.name, "alive", p.alive)
|
||||
|
||||
Only now, even after a player has died, the logger will still report they are alive because the logging
|
||||
context is evaluated when the logger was created. By using the Lazy wrapper, we can defer the evaluation
|
||||
of whether the player is alive or not to each log message, so that the log records will reflect the player's
|
||||
current state no matter when the log message is written:
|
||||
|
||||
p := &Player{name: name, alive: true}
|
||||
isAlive := func() bool { return p.alive }
|
||||
player.Logger = log.New("name", p.name, "alive", log.Lazy{isAlive})
|
||||
|
||||
Terminal Format
|
||||
|
||||
If log15 detects that stdout is a terminal, it will configure the default
|
||||
handler for it (which is log.StdoutHandler) to use TerminalFormat. This format
|
||||
logs records nicely for your terminal, including color-coded output based
|
||||
on log level.
|
||||
|
||||
Error Handling
|
||||
|
||||
Becasuse log15 allows you to step around the type system, there are a few ways you can specify
|
||||
invalid arguments to the logging functions. You could, for example, wrap something that is not
|
||||
a zero-argument function with log.Lazy or pass a context key that is not a string. Since logging libraries
|
||||
are typically the mechanism by which errors are reported, it would be onerous for the logging functions
|
||||
to return errors. Instead, log15 handles errors by making these guarantees to you:
|
||||
|
||||
- Any log record containing an error will still be printed with the error explained to you as part of the log record.
|
||||
|
||||
- Any log record containing an error will include the context key LOG15_ERROR, enabling you to easily
|
||||
(and if you like, automatically) detect if any of your logging calls are passing bad values.
|
||||
|
||||
Understanding this, you might wonder why the Handler interface can return an error value in its Log method. Handlers
|
||||
are encouraged to return errors only if they fail to write their log records out to an external source like if the
|
||||
syslog daemon is not responding. This allows the construction of useful handlers which cope with those failures
|
||||
like the FailoverHandler.
|
||||
|
||||
Library Use
|
||||
|
||||
log15 is intended to be useful for library authors as a way to provide configurable logging to
|
||||
users of their library. Best practice for use in a library is to always disable all output for your logger
|
||||
by default and to provide a public Logger instance that consumers of your library can configure. Like so:
|
||||
|
||||
package yourlib
|
||||
|
||||
import "github.com/inconshreveable/log15"
|
||||
|
||||
var Log = log.New()
|
||||
|
||||
func init() {
|
||||
Log.SetHandler(log.DiscardHandler())
|
||||
}
|
||||
|
||||
Users of your library may then enable it if they like:
|
||||
|
||||
import "github.com/inconshreveable/log15"
|
||||
import "example.com/yourlib"
|
||||
|
||||
func main() {
|
||||
handler := // custom handler setup
|
||||
yourlib.Log.SetHandler(handler)
|
||||
}
|
||||
|
||||
Best practices attaching logger context
|
||||
|
||||
The ability to attach context to a logger is a powerful one. Where should you do it and why?
|
||||
I favor embedding a Logger directly into any persistent object in my application and adding
|
||||
unique, tracing context keys to it. For instance, imagine I am writing a web browser:
|
||||
|
||||
type Tab struct {
|
||||
url string
|
||||
render *RenderingContext
|
||||
// ...
|
||||
|
||||
Logger
|
||||
}
|
||||
|
||||
func NewTab(url string) *Tab {
|
||||
return &Tab {
|
||||
// ...
|
||||
url: url,
|
||||
|
||||
Logger: log.New("url", url),
|
||||
}
|
||||
}
|
||||
|
||||
When a new tab is created, I assign a logger to it with the url of
|
||||
the tab as context so it can easily be traced through the logs.
|
||||
Now, whenever we perform any operation with the tab, we'll log with its
|
||||
embedded logger and it will include the tab title automatically:
|
||||
|
||||
tab.Debug("moved position", "idx", tab.idx)
|
||||
|
||||
There's only one problem. What if the tab url changes? We could
|
||||
use log.Lazy to make sure the current url is always written, but that
|
||||
would mean that we couldn't trace a tab's full lifetime through our
|
||||
logs after the user navigate to a new URL.
|
||||
|
||||
Instead, think about what values to attach to your loggers the
|
||||
same way you think about what to use as a key in a SQL database schema.
|
||||
If it's possible to use a natural key that is unique for the lifetime of the
|
||||
object, do so. But otherwise, log15's ext package has a handy RandId
|
||||
function to let you generate what you might call "surrogate keys"
|
||||
They're just random hex identifiers to use for tracing. Back to our
|
||||
Tab example, we would prefer to set up our Logger like so:
|
||||
|
||||
import logext "github.com/inconshreveable/log15/ext"
|
||||
|
||||
t := &Tab {
|
||||
// ...
|
||||
url: url,
|
||||
}
|
||||
|
||||
t.Logger = log.New("id", logext.RandId(8), "url", log.Lazy{t.getUrl})
|
||||
return t
|
||||
|
||||
Now we'll have a unique traceable identifier even across loading new urls, but
|
||||
we'll still be able to see the tab's current url in the log messages.
|
||||
|
||||
Must
|
||||
|
||||
For all Handler functions which can return an error, there is a version of that
|
||||
function which will return no error but panics on failure. They are all available
|
||||
on the Must object. For example:
|
||||
|
||||
log.Must.FileHandler("/path", log.JsonFormat)
|
||||
log.Must.NetHandler("tcp", ":1234", log.JsonFormat)
|
||||
|
||||
Inspiration and Credit
|
||||
|
||||
All of the following excellent projects inspired the design of this library:
|
||||
|
||||
code.google.com/p/log4go
|
||||
|
||||
github.com/op/go-logging
|
||||
|
||||
github.com/technoweenie/grohl
|
||||
|
||||
github.com/Sirupsen/logrus
|
||||
|
||||
github.com/kr/logfmt
|
||||
|
||||
github.com/spacemonkeygo/spacelog
|
||||
|
||||
golang's stdlib, notably io and net/http
|
||||
|
||||
The Name
|
||||
|
||||
https://xkcd.com/927/
|
||||
|
||||
*/
|
||||
package log
|
||||
622
log/format.go
622
log/format.go
|
|
@ -2,69 +2,26 @@ package log
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
const (
|
||||
timeFormat = "2006-01-02T15:04:05-0700"
|
||||
termTimeFormat = "01-02|15:04:05"
|
||||
floatFormat = 'f'
|
||||
termMsgJust = 40
|
||||
timeFormat = "2006-01-02T15:04:05-0700"
|
||||
floatFormat = 'f'
|
||||
termMsgJust = 40
|
||||
termCtxMaxPadding = 40
|
||||
)
|
||||
|
||||
// locationTrims are trimmed for display to avoid unwieldy log lines.
|
||||
var locationTrims = []string{
|
||||
"github.com/XinFinOrg/XDPoSChain/",
|
||||
}
|
||||
|
||||
// PrintOrigins sets or unsets log location (file:line) printing for terminal
|
||||
// format output.
|
||||
func PrintOrigins(print bool) {
|
||||
if print {
|
||||
atomic.StoreUint32(&locationEnabled, 1)
|
||||
} else {
|
||||
atomic.StoreUint32(&locationEnabled, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// locationEnabled is an atomic flag controlling whether the terminal formatter
|
||||
// should append the log locations too when printing entries.
|
||||
var locationEnabled uint32
|
||||
|
||||
// locationLength is the maxmimum path length encountered, which all logs are
|
||||
// padded to to aid in alignment.
|
||||
var locationLength uint32
|
||||
|
||||
// fieldPadding is a global map with maximum field value lengths seen until now
|
||||
// to allow padding log contexts in a bit smarter way.
|
||||
var fieldPadding = make(map[string]int)
|
||||
|
||||
// fieldPaddingLock is a global mutex protecting the field padding map.
|
||||
var fieldPaddingLock sync.RWMutex
|
||||
|
||||
type Format interface {
|
||||
Format(r *Record) []byte
|
||||
}
|
||||
|
||||
// FormatFunc returns a new Format object which uses
|
||||
// the given function to perform record formatting.
|
||||
func FormatFunc(f func(*Record) []byte) Format {
|
||||
return formatFunc(f)
|
||||
}
|
||||
|
||||
type formatFunc func(*Record) []byte
|
||||
|
||||
func (f formatFunc) Format(r *Record) []byte {
|
||||
return f(r)
|
||||
}
|
||||
// 40 spaces
|
||||
var spaces = []byte(" ")
|
||||
|
||||
// TerminalStringer is an analogous interface to the stdlib stringer, allowing
|
||||
// own types to have custom shortened serialization formats when printed to the
|
||||
|
|
@ -73,291 +30,334 @@ type TerminalStringer interface {
|
|||
TerminalString() string
|
||||
}
|
||||
|
||||
// TerminalFormat formats log records optimized for human readability on
|
||||
// a terminal with color-coded level output and terser human friendly timestamp.
|
||||
// This format should only be used for interactive programs or while developing.
|
||||
//
|
||||
// [TIME] [LEVEL] MESAGE key=value key=value ...
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// [May 16 20:58:45] [DBUG] remove route ns=haproxy addr=127.0.0.1:50002
|
||||
//
|
||||
func TerminalFormat(usecolor bool) Format {
|
||||
return FormatFunc(func(r *Record) []byte {
|
||||
var color = 0
|
||||
if usecolor {
|
||||
switch r.Lvl {
|
||||
case LvlCrit:
|
||||
color = 35
|
||||
case LvlError:
|
||||
color = 31
|
||||
case LvlWarn:
|
||||
color = 33
|
||||
case LvlInfo:
|
||||
color = 32
|
||||
case LvlDebug:
|
||||
color = 36
|
||||
case LvlTrace:
|
||||
color = 34
|
||||
}
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
lvl := r.Lvl.AlignedString()
|
||||
if atomic.LoadUint32(&locationEnabled) != 0 {
|
||||
// Log origin printing was requested, format the location path and line number
|
||||
location := fmt.Sprintf("%+v", r.Call)
|
||||
for _, prefix := range locationTrims {
|
||||
location = strings.TrimPrefix(location, prefix)
|
||||
}
|
||||
// Maintain the maximum location length for fancyer alignment
|
||||
align := int(atomic.LoadUint32(&locationLength))
|
||||
if align < len(location) {
|
||||
align = len(location)
|
||||
atomic.StoreUint32(&locationLength, uint32(align))
|
||||
}
|
||||
padding := strings.Repeat(" ", align-len(location))
|
||||
|
||||
// Assemble and print the log heading
|
||||
if color > 0 {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s|%s]%s %s ", color, lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg)
|
||||
}
|
||||
} else {
|
||||
if color > 0 {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg)
|
||||
}
|
||||
}
|
||||
// try to justify the log output for short messages
|
||||
length := utf8.RuneCountInString(r.Msg)
|
||||
if len(r.Ctx) > 0 && length < termMsgJust {
|
||||
b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length))
|
||||
}
|
||||
// print the keys logfmt style
|
||||
logfmt(b, r.Ctx, color, true)
|
||||
return b.Bytes()
|
||||
})
|
||||
}
|
||||
|
||||
// LogfmtFormat prints records in logfmt format, an easy machine-parseable but human-readable
|
||||
// format for key/value pairs.
|
||||
//
|
||||
// For more details see: http://godoc.org/github.com/kr/logfmt
|
||||
//
|
||||
func LogfmtFormat() Format {
|
||||
return FormatFunc(func(r *Record) []byte {
|
||||
common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg}
|
||||
buf := &bytes.Buffer{}
|
||||
logfmt(buf, append(common, r.Ctx...), 0, false)
|
||||
return buf.Bytes()
|
||||
})
|
||||
}
|
||||
|
||||
func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) {
|
||||
for i := 0; i < len(ctx); i += 2 {
|
||||
if i != 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
|
||||
k, ok := ctx[i].(string)
|
||||
v := formatLogfmtValue(ctx[i+1], term)
|
||||
if !ok {
|
||||
k, v = errorKey, formatLogfmtValue(k, term)
|
||||
}
|
||||
|
||||
// XXX: we should probably check that all of your key bytes aren't invalid
|
||||
fieldPaddingLock.RLock()
|
||||
padding := fieldPadding[k]
|
||||
fieldPaddingLock.RUnlock()
|
||||
|
||||
length := utf8.RuneCountInString(v)
|
||||
if padding < length {
|
||||
padding = length
|
||||
|
||||
fieldPaddingLock.Lock()
|
||||
fieldPadding[k] = padding
|
||||
fieldPaddingLock.Unlock()
|
||||
}
|
||||
if color > 0 {
|
||||
fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, k)
|
||||
} else {
|
||||
buf.WriteString(k)
|
||||
buf.WriteByte('=')
|
||||
}
|
||||
buf.WriteString(v)
|
||||
if i < len(ctx)-2 {
|
||||
buf.Write(bytes.Repeat([]byte{' '}, padding-length))
|
||||
func (h *TerminalHandler) format(buf []byte, r slog.Record, usecolor bool) []byte {
|
||||
msg := escapeMessage(r.Message)
|
||||
var color = ""
|
||||
if usecolor {
|
||||
switch r.Level {
|
||||
case LevelCrit:
|
||||
color = "\x1b[35m"
|
||||
case slog.LevelError:
|
||||
color = "\x1b[31m"
|
||||
case slog.LevelWarn:
|
||||
color = "\x1b[33m"
|
||||
case slog.LevelInfo:
|
||||
color = "\x1b[32m"
|
||||
case slog.LevelDebug:
|
||||
color = "\x1b[36m"
|
||||
case LevelTrace:
|
||||
color = "\x1b[34m"
|
||||
}
|
||||
}
|
||||
if buf == nil {
|
||||
buf = make([]byte, 0, 30+termMsgJust)
|
||||
}
|
||||
b := bytes.NewBuffer(buf)
|
||||
|
||||
if color != "" { // Start color
|
||||
b.WriteString(color)
|
||||
b.WriteString(LevelAlignedString(r.Level))
|
||||
b.WriteString("\x1b[0m")
|
||||
} else {
|
||||
b.WriteString(LevelAlignedString(r.Level))
|
||||
}
|
||||
b.WriteString("[")
|
||||
writeTimeTermFormat(b, r.Time)
|
||||
b.WriteString("] ")
|
||||
b.WriteString(msg)
|
||||
|
||||
// try to justify the log output for short messages
|
||||
//length := utf8.RuneCountInString(msg)
|
||||
length := len(msg)
|
||||
if (r.NumAttrs()+len(h.attrs)) > 0 && length < termMsgJust {
|
||||
b.Write(spaces[:termMsgJust-length])
|
||||
}
|
||||
// print the attributes
|
||||
h.formatAttributes(b, r, color)
|
||||
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func (h *TerminalHandler) formatAttributes(buf *bytes.Buffer, r slog.Record, color string) {
|
||||
writeAttr := func(attr slog.Attr, first, last bool) {
|
||||
buf.WriteByte(' ')
|
||||
|
||||
if color != "" {
|
||||
buf.WriteString(color)
|
||||
buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key))
|
||||
buf.WriteString("\x1b[0m=")
|
||||
} else {
|
||||
buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key))
|
||||
buf.WriteByte('=')
|
||||
}
|
||||
val := FormatSlogValue(attr.Value, buf.AvailableBuffer())
|
||||
|
||||
padding := h.fieldPadding[attr.Key]
|
||||
|
||||
length := utf8.RuneCount(val)
|
||||
if padding < length && length <= termCtxMaxPadding {
|
||||
padding = length
|
||||
h.fieldPadding[attr.Key] = padding
|
||||
}
|
||||
buf.Write(val)
|
||||
if !last && padding > length {
|
||||
buf.Write(spaces[:padding-length])
|
||||
}
|
||||
}
|
||||
var n = 0
|
||||
var nAttrs = len(h.attrs) + r.NumAttrs()
|
||||
for _, attr := range h.attrs {
|
||||
writeAttr(attr, n == 0, n == nAttrs-1)
|
||||
n++
|
||||
}
|
||||
r.Attrs(func(attr slog.Attr) bool {
|
||||
writeAttr(attr, n == 0, n == nAttrs-1)
|
||||
n++
|
||||
return true
|
||||
})
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
|
||||
// JsonFormat formats log records as JSON objects separated by newlines.
|
||||
// It is the equivalent of JsonFormatEx(false, true).
|
||||
func JsonFormat() Format {
|
||||
return JsonFormatEx(false, true)
|
||||
}
|
||||
|
||||
// JsonFormatEx formats log records as JSON objects. If pretty is true,
|
||||
// records will be pretty-printed. If lineSeparated is true, records
|
||||
// will be logged with a new line between each record.
|
||||
func JsonFormatEx(pretty, lineSeparated bool) Format {
|
||||
jsonMarshal := json.Marshal
|
||||
if pretty {
|
||||
jsonMarshal = func(v interface{}) ([]byte, error) {
|
||||
return json.MarshalIndent(v, "", " ")
|
||||
}
|
||||
}
|
||||
|
||||
return FormatFunc(func(r *Record) []byte {
|
||||
props := make(map[string]interface{})
|
||||
|
||||
props[r.KeyNames.Time] = r.Time
|
||||
props[r.KeyNames.Lvl] = r.Lvl.String()
|
||||
props[r.KeyNames.Msg] = r.Msg
|
||||
|
||||
for i := 0; i < len(r.Ctx); i += 2 {
|
||||
k, ok := r.Ctx[i].(string)
|
||||
if !ok {
|
||||
props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i])
|
||||
}
|
||||
props[k] = formatJsonValue(r.Ctx[i+1])
|
||||
}
|
||||
|
||||
b, err := jsonMarshal(props)
|
||||
if err != nil {
|
||||
b, _ = jsonMarshal(map[string]string{
|
||||
errorKey: err.Error(),
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
if lineSeparated {
|
||||
b = append(b, '\n')
|
||||
}
|
||||
|
||||
return b
|
||||
})
|
||||
}
|
||||
|
||||
func formatShared(value interface{}) (result interface{}) {
|
||||
// FormatSlogValue formats a slog.Value for serialization to terminal.
|
||||
func FormatSlogValue(v slog.Value, tmp []byte) (result []byte) {
|
||||
var value any
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
result = "nil"
|
||||
result = []byte("<nil>")
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
switch v := value.(type) {
|
||||
case time.Time:
|
||||
return v.Format(timeFormat)
|
||||
|
||||
case error:
|
||||
return v.Error()
|
||||
|
||||
case fmt.Stringer:
|
||||
return v.String()
|
||||
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func formatJsonValue(value interface{}) interface{} {
|
||||
value = formatShared(value)
|
||||
switch value.(type) {
|
||||
case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string:
|
||||
return value
|
||||
default:
|
||||
return fmt.Sprintf("%+v", value)
|
||||
}
|
||||
}
|
||||
|
||||
// formatValue formats a value for serialization
|
||||
func formatLogfmtValue(value interface{}, term bool) string {
|
||||
if value == nil {
|
||||
return "nil"
|
||||
}
|
||||
|
||||
if t, ok := value.(time.Time); ok {
|
||||
switch v.Kind() {
|
||||
case slog.KindString:
|
||||
return appendEscapeString(tmp, v.String())
|
||||
case slog.KindInt64: // All int-types (int8, int16 etc) wind up here
|
||||
return appendInt64(tmp, v.Int64())
|
||||
case slog.KindUint64: // All uint-types (uint8, uint16 etc) wind up here
|
||||
return appendUint64(tmp, v.Uint64(), false)
|
||||
case slog.KindFloat64:
|
||||
return strconv.AppendFloat(tmp, v.Float64(), floatFormat, 3, 64)
|
||||
case slog.KindBool:
|
||||
return strconv.AppendBool(tmp, v.Bool())
|
||||
case slog.KindDuration:
|
||||
value = v.Duration()
|
||||
case slog.KindTime:
|
||||
// Performance optimization: No need for escaping since the provided
|
||||
// timeFormat doesn't have any escape characters, and escaping is
|
||||
// expensive.
|
||||
return t.Format(timeFormat)
|
||||
}
|
||||
if term {
|
||||
if s, ok := value.(TerminalStringer); ok {
|
||||
// Custom terminal stringer provided, use that
|
||||
return escapeString(s.TerminalString())
|
||||
}
|
||||
}
|
||||
value = formatShared(value)
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
return strconv.FormatBool(v)
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(v), floatFormat, 3, 64)
|
||||
case float64:
|
||||
return strconv.FormatFloat(v, floatFormat, 3, 64)
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
return fmt.Sprintf("%d", value)
|
||||
case string:
|
||||
return escapeString(v)
|
||||
return v.Time().AppendFormat(tmp, timeFormat)
|
||||
default:
|
||||
return escapeString(fmt.Sprintf("%+v", value))
|
||||
value = v.Any()
|
||||
}
|
||||
if value == nil {
|
||||
return []byte("<nil>")
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case *big.Int: // Need to be before fmt.Stringer-clause
|
||||
return appendBigInt(tmp, v)
|
||||
case *uint256.Int: // Need to be before fmt.Stringer-clause
|
||||
return appendU256(tmp, v)
|
||||
case error:
|
||||
return appendEscapeString(tmp, v.Error())
|
||||
case TerminalStringer:
|
||||
return appendEscapeString(tmp, v.TerminalString())
|
||||
case fmt.Stringer:
|
||||
return appendEscapeString(tmp, v.String())
|
||||
}
|
||||
|
||||
// We can use the 'tmp' as a scratch-buffer, to first format the
|
||||
// value, and in a second step do escaping.
|
||||
internal := fmt.Appendf(tmp, "%+v", value)
|
||||
return appendEscapeString(tmp, string(internal))
|
||||
}
|
||||
|
||||
var stringBufPool = sync.Pool{
|
||||
New: func() interface{} { return new(bytes.Buffer) },
|
||||
// appendInt64 formats n with thousand separators and writes into buffer dst.
|
||||
func appendInt64(dst []byte, n int64) []byte {
|
||||
if n < 0 {
|
||||
return appendUint64(dst, uint64(-n), true)
|
||||
}
|
||||
return appendUint64(dst, uint64(n), false)
|
||||
}
|
||||
|
||||
func escapeString(s string) string {
|
||||
needsQuotes := false
|
||||
needsEscape := false
|
||||
// appendUint64 formats n with thousand separators and writes into buffer dst.
|
||||
func appendUint64(dst []byte, n uint64, neg bool) []byte {
|
||||
// Small numbers are fine as is
|
||||
if n < 100000 {
|
||||
if neg {
|
||||
return strconv.AppendInt(dst, -int64(n), 10)
|
||||
} else {
|
||||
return strconv.AppendInt(dst, int64(n), 10)
|
||||
}
|
||||
}
|
||||
// Large numbers should be split
|
||||
const maxLength = 26
|
||||
|
||||
var (
|
||||
out = make([]byte, maxLength)
|
||||
i = maxLength - 1
|
||||
comma = 0
|
||||
)
|
||||
for ; n > 0; i-- {
|
||||
if comma == 3 {
|
||||
comma = 0
|
||||
out[i] = ','
|
||||
} else {
|
||||
comma++
|
||||
out[i] = '0' + byte(n%10)
|
||||
n /= 10
|
||||
}
|
||||
}
|
||||
if neg {
|
||||
out[i] = '-'
|
||||
i--
|
||||
}
|
||||
return append(dst, out[i+1:]...)
|
||||
}
|
||||
|
||||
// FormatLogfmtUint64 formats n with thousand separators.
|
||||
func FormatLogfmtUint64(n uint64) string {
|
||||
return string(appendUint64(nil, n, false))
|
||||
}
|
||||
|
||||
// appendBigInt formats n with thousand separators and writes to dst.
|
||||
func appendBigInt(dst []byte, n *big.Int) []byte {
|
||||
if n.IsUint64() {
|
||||
return appendUint64(dst, n.Uint64(), false)
|
||||
}
|
||||
if n.IsInt64() {
|
||||
return appendInt64(dst, n.Int64())
|
||||
}
|
||||
|
||||
var (
|
||||
text = n.String()
|
||||
buf = make([]byte, len(text)+len(text)/3)
|
||||
comma = 0
|
||||
i = len(buf) - 1
|
||||
)
|
||||
for j := len(text) - 1; j >= 0; j, i = j-1, i-1 {
|
||||
c := text[j]
|
||||
|
||||
switch {
|
||||
case c == '-':
|
||||
buf[i] = c
|
||||
case comma == 3:
|
||||
buf[i] = ','
|
||||
i--
|
||||
comma = 0
|
||||
fallthrough
|
||||
default:
|
||||
buf[i] = c
|
||||
comma++
|
||||
}
|
||||
}
|
||||
return append(dst, buf[i+1:]...)
|
||||
}
|
||||
|
||||
// appendU256 formats n with thousand separators.
|
||||
func appendU256(dst []byte, n *uint256.Int) []byte {
|
||||
if n.IsUint64() {
|
||||
return appendUint64(dst, n.Uint64(), false)
|
||||
}
|
||||
res := []byte(n.PrettyDec(','))
|
||||
return append(dst, res...)
|
||||
}
|
||||
|
||||
// appendEscapeString writes the string s to the given writer, with
|
||||
// escaping/quoting if needed.
|
||||
func appendEscapeString(dst []byte, s string) []byte {
|
||||
needsQuoting := false
|
||||
needsEscaping := false
|
||||
for _, r := range s {
|
||||
if r <= ' ' || r == '=' || r == '"' {
|
||||
needsQuotes = true
|
||||
// If it contains spaces or equal-sign, we need to quote it.
|
||||
if r == ' ' || r == '=' {
|
||||
needsQuoting = true
|
||||
continue
|
||||
}
|
||||
if r == '\\' || r == '"' || r == '\n' || r == '\r' || r == '\t' {
|
||||
needsEscape = true
|
||||
// We need to escape it, if it contains
|
||||
// - character " (0x22) and lower (except space)
|
||||
// - characters above ~ (0x7E), plus equal-sign
|
||||
if r <= '"' || r > '~' {
|
||||
needsEscaping = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !needsEscape && !needsQuotes {
|
||||
if needsEscaping {
|
||||
return strconv.AppendQuote(dst, s)
|
||||
}
|
||||
// No escaping needed, but we might have to place within quote-marks, in case
|
||||
// it contained a space
|
||||
if needsQuoting {
|
||||
dst = append(dst, '"')
|
||||
dst = append(dst, []byte(s)...)
|
||||
return append(dst, '"')
|
||||
}
|
||||
return append(dst, []byte(s)...)
|
||||
}
|
||||
|
||||
// escapeMessage checks if the provided string needs escaping/quoting, similarly
|
||||
// to escapeString. The difference is that this method is more lenient: it allows
|
||||
// for spaces and linebreaks to occur without needing quoting.
|
||||
func escapeMessage(s string) string {
|
||||
needsQuoting := false
|
||||
for _, r := range s {
|
||||
// Allow CR/LF/TAB. This is to make multi-line messages work.
|
||||
if r == '\r' || r == '\n' || r == '\t' {
|
||||
continue
|
||||
}
|
||||
// We quote everything below <space> (0x20) and above~ (0x7E),
|
||||
// plus equal-sign
|
||||
if r < ' ' || r > '~' || r == '=' {
|
||||
needsQuoting = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !needsQuoting {
|
||||
return s
|
||||
}
|
||||
e := stringBufPool.Get().(*bytes.Buffer)
|
||||
e.WriteByte('"')
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case '\\', '"':
|
||||
e.WriteByte('\\')
|
||||
e.WriteByte(byte(r))
|
||||
case '\n':
|
||||
e.WriteString("\\n")
|
||||
case '\r':
|
||||
e.WriteString("\\r")
|
||||
case '\t':
|
||||
e.WriteString("\\t")
|
||||
default:
|
||||
e.WriteRune(r)
|
||||
}
|
||||
}
|
||||
e.WriteByte('"')
|
||||
var ret string
|
||||
if needsQuotes {
|
||||
ret = e.String()
|
||||
} else {
|
||||
ret = string(e.Bytes()[1 : e.Len()-1])
|
||||
}
|
||||
e.Reset()
|
||||
stringBufPool.Put(e)
|
||||
return ret
|
||||
return strconv.Quote(s)
|
||||
}
|
||||
|
||||
// writeTimeTermFormat writes on the format "01-02|15:04:05.000"
|
||||
func writeTimeTermFormat(buf *bytes.Buffer, t time.Time) {
|
||||
_, month, day := t.Date()
|
||||
writePosIntWidth(buf, int(month), 2)
|
||||
buf.WriteByte('-')
|
||||
writePosIntWidth(buf, day, 2)
|
||||
buf.WriteByte('|')
|
||||
hour, min, sec := t.Clock()
|
||||
writePosIntWidth(buf, hour, 2)
|
||||
buf.WriteByte(':')
|
||||
writePosIntWidth(buf, min, 2)
|
||||
buf.WriteByte(':')
|
||||
writePosIntWidth(buf, sec, 2)
|
||||
ns := t.Nanosecond()
|
||||
buf.WriteByte('.')
|
||||
writePosIntWidth(buf, ns/1e6, 3)
|
||||
}
|
||||
|
||||
// writePosIntWidth writes non-negative integer i to the buffer, padded on the left
|
||||
// by zeroes to the given width. Use a width of 0 to omit padding.
|
||||
// Adapted from pkg.go.dev/log/slog/internal/buffer
|
||||
func writePosIntWidth(b *bytes.Buffer, i, width int) {
|
||||
// Cheap integer to fixed-width decimal ASCII.
|
||||
// Copied from log/log.go.
|
||||
if i < 0 {
|
||||
panic("negative int")
|
||||
}
|
||||
// Assemble decimal in reverse order.
|
||||
var bb [20]byte
|
||||
bp := len(bb) - 1
|
||||
for i >= 10 || width > 1 {
|
||||
width--
|
||||
q := i / 10
|
||||
bb[bp] = byte('0' + i - q*10)
|
||||
bp--
|
||||
i = q
|
||||
}
|
||||
// i < 10
|
||||
bb[bp] = byte('0' + i)
|
||||
b.Write(bb[bp:])
|
||||
}
|
||||
|
|
|
|||
24
log/format_test.go
Normal file
24
log/format_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var sink []byte
|
||||
|
||||
func BenchmarkPrettyInt64Logfmt(b *testing.B) {
|
||||
buf := make([]byte, 100)
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink = appendInt64(buf, rand.Int63())
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrettyUint64Logfmt(b *testing.B) {
|
||||
buf := make([]byte, 100)
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink = appendUint64(buf, rand.Uint64(), false)
|
||||
}
|
||||
}
|
||||
456
log/handler.go
456
log/handler.go
|
|
@ -1,361 +1,199 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"log/slog"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-stack/stack"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// A Logger prints its log records by writing to a Handler.
|
||||
// The Handler interface defines where and how log records are written.
|
||||
// Handlers are composable, providing you great flexibility in combining
|
||||
// them to achieve the logging structure that suits your applications.
|
||||
type Handler interface {
|
||||
Log(r *Record) error
|
||||
type discardHandler struct{}
|
||||
|
||||
// DiscardHandler returns a no-op handler
|
||||
func DiscardHandler() slog.Handler {
|
||||
return &discardHandler{}
|
||||
}
|
||||
|
||||
// FuncHandler returns a Handler that logs records with the given
|
||||
// function.
|
||||
func FuncHandler(fn func(r *Record) error) Handler {
|
||||
return funcHandler(fn)
|
||||
func (h *discardHandler) Handle(_ context.Context, r slog.Record) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type funcHandler func(r *Record) error
|
||||
|
||||
func (h funcHandler) Log(r *Record) error {
|
||||
return h(r)
|
||||
func (h *discardHandler) Enabled(_ context.Context, level slog.Level) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// StreamHandler writes log records to an io.Writer
|
||||
// with the given format. StreamHandler can be used
|
||||
// to easily begin writing log records to other
|
||||
// outputs.
|
||||
func (h *discardHandler) WithGroup(name string) slog.Handler {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (h *discardHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
return &discardHandler{}
|
||||
}
|
||||
|
||||
type TerminalHandler struct {
|
||||
mu sync.Mutex
|
||||
wr io.Writer
|
||||
lvl slog.Level
|
||||
useColor bool
|
||||
attrs []slog.Attr
|
||||
// fieldPadding is a map with maximum field value lengths seen until now
|
||||
// to allow padding log contexts in a bit smarter way.
|
||||
fieldPadding map[string]int
|
||||
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// NewTerminalHandler returns a handler which formats log records at all levels optimized for human readability on
|
||||
// a terminal with color-coded level output and terser human friendly timestamp.
|
||||
// This format should only be used for interactive programs or while developing.
|
||||
//
|
||||
// StreamHandler wraps itself with LazyHandler and SyncHandler
|
||||
// to evaluate Lazy objects and perform safe concurrent writes.
|
||||
func StreamHandler(wr io.Writer, fmtr Format) Handler {
|
||||
h := FuncHandler(func(r *Record) error {
|
||||
_, err := wr.Write(fmtr.Format(r))
|
||||
return err
|
||||
})
|
||||
return LazyHandler(SyncHandler(h))
|
||||
// [LEVEL] [TIME] MESSAGE key=value key=value ...
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002
|
||||
func NewTerminalHandler(wr io.Writer, useColor bool) *TerminalHandler {
|
||||
return NewTerminalHandlerWithLevel(wr, levelMaxVerbosity, useColor)
|
||||
}
|
||||
|
||||
// SyncHandler can be wrapped around a handler to guarantee that
|
||||
// only a single Log operation can proceed at a time. It's necessary
|
||||
// for thread-safe concurrent writes.
|
||||
func SyncHandler(h Handler) Handler {
|
||||
var mu sync.Mutex
|
||||
return FuncHandler(func(r *Record) error {
|
||||
defer mu.Unlock()
|
||||
mu.Lock()
|
||||
return h.Log(r)
|
||||
})
|
||||
}
|
||||
|
||||
// FileHandler returns a handler which writes log records to the give file
|
||||
// using the given format. If the path
|
||||
// already exists, FileHandler will append to the given file. If it does not,
|
||||
// FileHandler will create the file with mode 0644.
|
||||
func FileHandler(path string, fmtr Format) (Handler, error) {
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// NewTerminalHandlerWithLevel returns the same handler as NewTerminalHandler but only outputs
|
||||
// records which are less than or equal to the specified verbosity level.
|
||||
func NewTerminalHandlerWithLevel(wr io.Writer, lvl slog.Level, useColor bool) *TerminalHandler {
|
||||
return &TerminalHandler{
|
||||
wr: wr,
|
||||
lvl: lvl,
|
||||
useColor: useColor,
|
||||
fieldPadding: make(map[string]int),
|
||||
}
|
||||
return closingHandler{f, StreamHandler(f, fmtr)}, nil
|
||||
}
|
||||
|
||||
// NetHandler opens a socket to the given address and writes records
|
||||
// over the connection.
|
||||
func NetHandler(network, addr string, fmtr Format) (Handler, error) {
|
||||
conn, err := net.Dial(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (h *TerminalHandler) Handle(_ context.Context, r slog.Record) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
buf := h.format(h.buf, r, h.useColor)
|
||||
h.wr.Write(buf)
|
||||
h.buf = buf[:0]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *TerminalHandler) Enabled(_ context.Context, level slog.Level) bool {
|
||||
return level >= h.lvl
|
||||
}
|
||||
|
||||
func (h *TerminalHandler) WithGroup(name string) slog.Handler {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (h *TerminalHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
return &TerminalHandler{
|
||||
wr: h.wr,
|
||||
lvl: h.lvl,
|
||||
useColor: h.useColor,
|
||||
attrs: append(h.attrs, attrs...),
|
||||
fieldPadding: make(map[string]int),
|
||||
}
|
||||
|
||||
return closingHandler{conn, StreamHandler(conn, fmtr)}, nil
|
||||
}
|
||||
|
||||
// XXX: closingHandler is essentially unused at the moment
|
||||
// it's meant for a future time when the Handler interface supports
|
||||
// a possible Close() operation
|
||||
type closingHandler struct {
|
||||
io.WriteCloser
|
||||
Handler
|
||||
// ResetFieldPadding zeroes the field-padding for all attribute pairs.
|
||||
func (h *TerminalHandler) ResetFieldPadding() {
|
||||
h.mu.Lock()
|
||||
h.fieldPadding = make(map[string]int)
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
func (h *closingHandler) Close() error {
|
||||
return h.WriteCloser.Close()
|
||||
type leveler struct{ minLevel slog.Level }
|
||||
|
||||
func (l *leveler) Level() slog.Level {
|
||||
return l.minLevel
|
||||
}
|
||||
|
||||
// CallerFileHandler returns a Handler that adds the line number and file of
|
||||
// the calling function to the context with key "caller".
|
||||
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)
|
||||
// JSONHandler returns a handler which prints records in JSON format.
|
||||
func JSONHandler(wr io.Writer) slog.Handler {
|
||||
return JSONHandlerWithLevel(wr, levelMaxVerbosity)
|
||||
}
|
||||
|
||||
// JSONHandlerWithLevel returns a handler which prints records in JSON format that are less than or equal to
|
||||
// the specified verbosity level.
|
||||
func JSONHandlerWithLevel(wr io.Writer, level slog.Level) slog.Handler {
|
||||
return slog.NewJSONHandler(wr, &slog.HandlerOptions{
|
||||
ReplaceAttr: builtinReplaceJSON,
|
||||
Level: &leveler{level},
|
||||
})
|
||||
}
|
||||
|
||||
// CallerFuncHandler returns a Handler that adds the calling function name to
|
||||
// the context with key "fn".
|
||||
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)
|
||||
// LogfmtHandler returns a handler which prints records in logfmt format, an easy machine-parseable but human-readable
|
||||
// format for key/value pairs.
|
||||
//
|
||||
// For more details see: http://godoc.org/github.com/kr/logfmt
|
||||
func LogfmtHandler(wr io.Writer) slog.Handler {
|
||||
return slog.NewTextHandler(wr, &slog.HandlerOptions{
|
||||
ReplaceAttr: builtinReplaceLogfmt,
|
||||
})
|
||||
}
|
||||
|
||||
// This function is here to please go vet on Go < 1.8.
|
||||
func formatCall(format string, c stack.Call) string {
|
||||
return fmt.Sprintf(format, c)
|
||||
}
|
||||
|
||||
// CallerStackHandler returns a Handler that adds a stack trace to the context
|
||||
// with key "stack". The stack trace is formated as a space separated list of
|
||||
// call sites inside matching []'s. The most recent call site is listed first.
|
||||
// Each call site is formatted according to format. See the documentation of
|
||||
// package github.com/go-stack/stack for the list of supported formats.
|
||||
func CallerStackHandler(format string, h Handler) Handler {
|
||||
return FuncHandler(func(r *Record) error {
|
||||
s := stack.Trace().TrimBelow(r.Call).TrimRuntime()
|
||||
if len(s) > 0 {
|
||||
r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s))
|
||||
}
|
||||
return h.Log(r)
|
||||
// LogfmtHandlerWithLevel returns the same handler as LogfmtHandler but it only outputs
|
||||
// records which are less than or equal to the specified verbosity level.
|
||||
func LogfmtHandlerWithLevel(wr io.Writer, level slog.Level) slog.Handler {
|
||||
return slog.NewTextHandler(wr, &slog.HandlerOptions{
|
||||
ReplaceAttr: builtinReplaceLogfmt,
|
||||
Level: &leveler{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))
|
||||
//
|
||||
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
|
||||
})
|
||||
func builtinReplaceLogfmt(_ []string, attr slog.Attr) slog.Attr {
|
||||
return builtinReplace(nil, attr, true)
|
||||
}
|
||||
|
||||
// MatchFilterHandler returns a Handler that only writes records
|
||||
// to the wrapped Handler if the given key in the logged
|
||||
// context matches the value. For example, to only log records
|
||||
// from your ui package:
|
||||
//
|
||||
// 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 {
|
||||
case r.KeyNames.Lvl:
|
||||
return r.Lvl == value
|
||||
case r.KeyNames.Time:
|
||||
return r.Time == value
|
||||
case r.KeyNames.Msg:
|
||||
return r.Msg == value
|
||||
}
|
||||
|
||||
for i := 0; i < len(r.Ctx); i += 2 {
|
||||
if r.Ctx[i] == key {
|
||||
return r.Ctx[i+1] == value
|
||||
}
|
||||
}
|
||||
return false
|
||||
}, h)
|
||||
func builtinReplaceJSON(_ []string, attr slog.Attr) slog.Attr {
|
||||
return builtinReplace(nil, attr, false)
|
||||
}
|
||||
|
||||
// LvlFilterHandler returns a Handler that only writes
|
||||
// records which are less than the given verbosity
|
||||
// level to the wrapped Handler. For example, to only
|
||||
// log Error/Crit records:
|
||||
//
|
||||
// log.LvlFilterHandler(log.LvlError, log.StdoutHandler)
|
||||
//
|
||||
func LvlFilterHandler(maxLvl Lvl, h Handler) Handler {
|
||||
return FilterHandler(func(r *Record) (pass bool) {
|
||||
return r.Lvl <= maxLvl
|
||||
}, h)
|
||||
}
|
||||
|
||||
// A MultiHandler dispatches any write to each of its handlers.
|
||||
// This is useful for writing different types of log information
|
||||
// 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)
|
||||
//
|
||||
func MultiHandler(hs ...Handler) Handler {
|
||||
return FuncHandler(func(r *Record) error {
|
||||
for _, h := range hs {
|
||||
// what to do about failures?
|
||||
h.Log(r)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// A FailoverHandler writes all log records to the first handler
|
||||
// specified, but will failover and write to the second handler if
|
||||
// the first handler has failed, and so on for all handlers specified.
|
||||
// For example you might want to log to a network socket, but failover
|
||||
// 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)
|
||||
//
|
||||
// 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
|
||||
// trying to write to the handlers before them in the list.
|
||||
func FailoverHandler(hs ...Handler) Handler {
|
||||
return FuncHandler(func(r *Record) error {
|
||||
var err error
|
||||
for i, h := range hs {
|
||||
err = h.Log(r)
|
||||
if err == nil {
|
||||
return nil
|
||||
func builtinReplace(_ []string, attr slog.Attr, logfmt bool) slog.Attr {
|
||||
switch attr.Key {
|
||||
case slog.TimeKey:
|
||||
if attr.Value.Kind() == slog.KindTime {
|
||||
if logfmt {
|
||||
return slog.String("t", attr.Value.Time().Format(timeFormat))
|
||||
} else {
|
||||
r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err)
|
||||
return slog.Attr{Key: "t", Value: attr.Value}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return FuncHandler(func(r *Record) error {
|
||||
recs <- r
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// BufferedHandler writes all records to a buffered
|
||||
// channel of the given size which flushes into the wrapped
|
||||
// handler whenever it is available for writing. Since these
|
||||
// writes happen asynchronously, all writes to a BufferedHandler
|
||||
// never return an error and any errors from the wrapped handler are ignored.
|
||||
func BufferedHandler(bufSize int, h Handler) Handler {
|
||||
recs := make(chan *Record, bufSize)
|
||||
go func() {
|
||||
for m := range recs {
|
||||
_ = h.Log(m)
|
||||
case slog.LevelKey:
|
||||
if l, ok := attr.Value.Any().(slog.Level); ok {
|
||||
attr = slog.Any("lvl", LevelString(l))
|
||||
return attr
|
||||
}
|
||||
}()
|
||||
return ChannelHandler(recs)
|
||||
}
|
||||
}
|
||||
|
||||
// LazyHandler writes all values to the wrapped handler after evaluating
|
||||
// any lazy functions in the record's context. It is already wrapped
|
||||
// around StreamHandler and SyslogHandler in this library, you'll only need
|
||||
// it if you write your own Handler.
|
||||
func LazyHandler(h Handler) Handler {
|
||||
return FuncHandler(func(r *Record) error {
|
||||
// go through the values (odd indices) and reassign
|
||||
// the values of any lazy fn to the result of its execution
|
||||
hadErr := false
|
||||
for i := 1; i < len(r.Ctx); i += 2 {
|
||||
lz, ok := r.Ctx[i].(Lazy)
|
||||
if ok {
|
||||
v, err := evaluateLazy(lz)
|
||||
if err != nil {
|
||||
hadErr = true
|
||||
r.Ctx[i] = err
|
||||
} else {
|
||||
if cs, ok := v.(stack.CallStack); ok {
|
||||
v = cs.TrimBelow(r.Call).TrimRuntime()
|
||||
}
|
||||
r.Ctx[i] = v
|
||||
}
|
||||
}
|
||||
switch v := attr.Value.Any().(type) {
|
||||
case time.Time:
|
||||
if logfmt {
|
||||
attr = slog.String(attr.Key, v.Format(timeFormat))
|
||||
}
|
||||
|
||||
if hadErr {
|
||||
r.Ctx = append(r.Ctx, errorKey, "bad lazy")
|
||||
case *big.Int:
|
||||
if v == nil {
|
||||
attr.Value = slog.StringValue("<nil>")
|
||||
} else {
|
||||
attr.Value = slog.StringValue(v.String())
|
||||
}
|
||||
|
||||
return h.Log(r)
|
||||
})
|
||||
}
|
||||
|
||||
func evaluateLazy(lz Lazy) (interface{}, error) {
|
||||
t := reflect.TypeOf(lz.Fn)
|
||||
|
||||
if t.Kind() != reflect.Func {
|
||||
return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn)
|
||||
}
|
||||
|
||||
if t.NumIn() > 0 {
|
||||
return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn)
|
||||
}
|
||||
|
||||
if t.NumOut() == 0 {
|
||||
return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn)
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(lz.Fn)
|
||||
results := value.Call([]reflect.Value{})
|
||||
if len(results) == 1 {
|
||||
return results[0].Interface(), nil
|
||||
} else {
|
||||
values := make([]interface{}, len(results))
|
||||
for i, v := range results {
|
||||
values[i] = v.Interface()
|
||||
case *uint256.Int:
|
||||
if v == nil {
|
||||
attr.Value = slog.StringValue("<nil>")
|
||||
} else {
|
||||
attr.Value = slog.StringValue(v.Dec())
|
||||
}
|
||||
case fmt.Stringer:
|
||||
if v == nil || (reflect.ValueOf(v).Kind() == reflect.Pointer && reflect.ValueOf(v).IsNil()) {
|
||||
attr.Value = slog.StringValue("<nil>")
|
||||
} else {
|
||||
attr.Value = slog.StringValue(v.String())
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
}
|
||||
|
||||
// DiscardHandler reports success for all writes but does nothing.
|
||||
// It is useful for dynamically disabling logging at runtime via
|
||||
// a Logger's SetHandler method.
|
||||
func DiscardHandler() Handler {
|
||||
return FuncHandler(func(r *Record) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// The Must object provides the following Handler creation functions
|
||||
// which instead of returning an error parameter only return a Handler
|
||||
// and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler
|
||||
var Must muster
|
||||
|
||||
func must(h Handler, err error) Handler {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
type muster struct{}
|
||||
|
||||
func (m muster) FileHandler(path string, fmtr Format) Handler {
|
||||
return must(FileHandler(path, fmtr))
|
||||
}
|
||||
|
||||
func (m muster) NetHandler(network, addr string, fmtr Format) Handler {
|
||||
return must(NetHandler(network, addr, fmtr))
|
||||
return attr
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,11 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
|
@ -30,28 +33,24 @@ import (
|
|||
// errVmoduleSyntax is returned when a user vmodule pattern is invalid.
|
||||
var errVmoduleSyntax = errors.New("expect comma-separated list of filename=N")
|
||||
|
||||
// errTraceSyntax is returned when a user backtrace pattern is invalid.
|
||||
var errTraceSyntax = errors.New("expect file.go:234")
|
||||
|
||||
// GlogHandler is a log handler that mimics the filtering features of Google's
|
||||
// glog logger: setting global log levels; overriding with callsite pattern
|
||||
// matches; and requesting backtraces at certain positions.
|
||||
type GlogHandler struct {
|
||||
origin Handler // The origin handler this wraps
|
||||
origin slog.Handler // The origin handler this wraps
|
||||
|
||||
level uint32 // Current log level, atomically accessible
|
||||
override uint32 // Flag whether overrides are used, atomically accessible
|
||||
backtrace uint32 // Flag whether backtrace location is set
|
||||
level atomic.Int32 // Current log level, atomically accessible
|
||||
override atomic.Bool // Flag whether overrides are used, atomically accessible
|
||||
|
||||
patterns []pattern // Current list of patterns to override with
|
||||
siteCache map[uintptr]Lvl // Cache of callsite pattern evaluations
|
||||
location string // file:line location where to do a stackdump at
|
||||
lock sync.RWMutex // Lock protecting the override pattern list
|
||||
patterns []pattern // Current list of patterns to override with
|
||||
siteCache map[uintptr]slog.Level // Cache of callsite pattern evaluations
|
||||
location string // file:line location where to do a stackdump at
|
||||
lock sync.RWMutex // Lock protecting the override pattern list
|
||||
}
|
||||
|
||||
// NewGlogHandler creates a new log handler with filtering functionality similar
|
||||
// to Google's glog logger. The returned handler implements Handler.
|
||||
func NewGlogHandler(h Handler) *GlogHandler {
|
||||
func NewGlogHandler(h slog.Handler) *GlogHandler {
|
||||
return &GlogHandler{
|
||||
origin: h,
|
||||
}
|
||||
|
|
@ -61,13 +60,13 @@ func NewGlogHandler(h Handler) *GlogHandler {
|
|||
// and a file pattern to match.
|
||||
type pattern struct {
|
||||
pattern *regexp.Regexp
|
||||
level Lvl
|
||||
level slog.Level
|
||||
}
|
||||
|
||||
// Verbosity sets the glog verbosity ceiling. The verbosity of individual packages
|
||||
// and source files can be raised using Vmodule.
|
||||
func (h *GlogHandler) Verbosity(level Lvl) {
|
||||
atomic.StoreUint32(&h.level, uint32(level))
|
||||
func (h *GlogHandler) Verbosity(level slog.Level) {
|
||||
h.level.Store(int32(level))
|
||||
}
|
||||
|
||||
// Vmodule sets the glog verbosity pattern.
|
||||
|
|
@ -77,14 +76,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, ",") {
|
||||
|
|
@ -103,11 +102,13 @@ func (h *GlogHandler) Vmodule(ruleset string) error {
|
|||
return errVmoduleSyntax
|
||||
}
|
||||
// Parse the level and if correct, assemble the filter rule
|
||||
level, err := strconv.Atoi(parts[1])
|
||||
l, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return errVmoduleSyntax
|
||||
}
|
||||
if level <= 0 {
|
||||
level := FromLegacyLevel(l)
|
||||
|
||||
if level == LevelCrit {
|
||||
continue // Ignore. It's harmless but no point in paying the overhead.
|
||||
}
|
||||
// Compile the rule pattern into a regular expression
|
||||
|
|
@ -125,103 +126,89 @@ func (h *GlogHandler) Vmodule(ruleset string) error {
|
|||
matcher = matcher + "$"
|
||||
|
||||
re, _ := regexp.Compile(matcher)
|
||||
filter = append(filter, pattern{re, Lvl(level)})
|
||||
filter = append(filter, pattern{re, level})
|
||||
}
|
||||
// Swap out the vmodule pattern for the new filter system
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
h.patterns = filter
|
||||
h.siteCache = make(map[uintptr]Lvl)
|
||||
atomic.StoreUint32(&h.override, uint32(len(filter)))
|
||||
h.siteCache = make(map[uintptr]slog.Level)
|
||||
h.override.Store(len(filter) != 0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BacktraceAt sets the glog backtrace location. When set to a file and line
|
||||
// number holding a logging statement, a stack trace will be written to the Info
|
||||
// log whenever execution hits that statement.
|
||||
// Enabled implements slog.Handler, reporting whether the handler handles records
|
||||
// at the given level.
|
||||
func (h *GlogHandler) Enabled(ctx context.Context, lvl slog.Level) bool {
|
||||
// fast-track skipping logging if override not enabled and the provided verbosity is above configured
|
||||
return h.override.Load() || slog.Level(h.level.Load()) <= lvl
|
||||
}
|
||||
|
||||
// WithAttrs implements slog.Handler, returning a new Handler whose attributes
|
||||
// consist of both the receiver's attributes and the arguments.
|
||||
func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
h.lock.RLock()
|
||||
siteCache := maps.Clone(h.siteCache)
|
||||
h.lock.RUnlock()
|
||||
|
||||
patterns := []pattern{}
|
||||
patterns = append(patterns, h.patterns...)
|
||||
|
||||
res := GlogHandler{
|
||||
origin: h.origin.WithAttrs(attrs),
|
||||
patterns: patterns,
|
||||
siteCache: siteCache,
|
||||
location: h.location,
|
||||
}
|
||||
|
||||
res.level.Store(h.level.Load())
|
||||
res.override.Store(h.override.Load())
|
||||
return &res
|
||||
}
|
||||
|
||||
// WithGroup implements slog.Handler, returning a new Handler with the given
|
||||
// group appended to the receiver's existing groups.
|
||||
//
|
||||
// Unlike with Vmodule, the ".go" must be present.
|
||||
func (h *GlogHandler) BacktraceAt(location string) error {
|
||||
// Ensure the backtrace location contains two non-empty elements
|
||||
parts := strings.Split(location, ":")
|
||||
if len(parts) != 2 {
|
||||
return errTraceSyntax
|
||||
}
|
||||
parts[0] = strings.TrimSpace(parts[0])
|
||||
parts[1] = strings.TrimSpace(parts[1])
|
||||
if len(parts[0]) == 0 || len(parts[1]) == 0 {
|
||||
return errTraceSyntax
|
||||
}
|
||||
// Ensure the .go prefix is present and the line is valid
|
||||
if !strings.HasSuffix(parts[0], ".go") {
|
||||
return errTraceSyntax
|
||||
}
|
||||
if _, err := strconv.Atoi(parts[1]); err != nil {
|
||||
return errTraceSyntax
|
||||
}
|
||||
// All seems valid
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
h.location = location
|
||||
atomic.StoreUint32(&h.backtrace, uint32(len(location)))
|
||||
|
||||
return nil
|
||||
// Note, this function is not implemented.
|
||||
func (h *GlogHandler) WithGroup(name string) slog.Handler {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// Log implements Handler.Log, filtering a log record through the global, local
|
||||
// and backtrace filters, finally emitting it if either allow it through.
|
||||
func (h *GlogHandler) Log(r *Record) error {
|
||||
// If backtracing is requested, check whether this is the callsite
|
||||
if atomic.LoadUint32(&h.backtrace) > 0 {
|
||||
// Everything below here is slow. Although we could cache the call sites the
|
||||
// same way as for vmodule, backtracing is so rare it's not worth the extra
|
||||
// complexity.
|
||||
h.lock.RLock()
|
||||
match := h.location == r.Call.String()
|
||||
h.lock.RUnlock()
|
||||
|
||||
if match {
|
||||
// Callsite matched, raise the log level to info and gather the stacks
|
||||
r.Lvl = LvlInfo
|
||||
|
||||
buf := make([]byte, 1024*1024)
|
||||
buf = buf[:runtime.Stack(buf, true)]
|
||||
r.Msg += "\n\n" + string(buf)
|
||||
}
|
||||
}
|
||||
// Handle implements slog.Handler, filtering a log record through the global,
|
||||
// local and backtrace filters, finally emitting it if either allow it through.
|
||||
func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error {
|
||||
// If the global log level allows, fast track logging
|
||||
if atomic.LoadUint32(&h.level) >= uint32(r.Lvl) {
|
||||
return h.origin.Log(r)
|
||||
}
|
||||
// If no local overrides are present, fast track skipping
|
||||
if atomic.LoadUint32(&h.override) == 0 {
|
||||
return nil
|
||||
if slog.Level(h.level.Load()) <= r.Level {
|
||||
return h.origin.Handle(context.Background(), r)
|
||||
}
|
||||
|
||||
// Check callsite cache for previously calculated log levels
|
||||
h.lock.RLock()
|
||||
lvl, ok := h.siteCache[r.Call.PC()]
|
||||
lvl, ok := h.siteCache[r.PC]
|
||||
h.lock.RUnlock()
|
||||
|
||||
// If we didn't cache the callsite yet, calculate it
|
||||
if !ok {
|
||||
h.lock.Lock()
|
||||
|
||||
fs := runtime.CallersFrames([]uintptr{r.PC})
|
||||
frame, _ := fs.Next()
|
||||
|
||||
for _, rule := range h.patterns {
|
||||
if rule.pattern.MatchString(fmt.Sprintf("%+s", r.Call)) {
|
||||
h.siteCache[r.Call.PC()], lvl, ok = rule.level, rule.level, true
|
||||
break
|
||||
if rule.pattern.MatchString(fmt.Sprintf("+%s", frame.File)) {
|
||||
h.siteCache[r.PC], lvl, ok = rule.level, rule.level, true
|
||||
}
|
||||
}
|
||||
// If no rule matched, remember to drop log the next time
|
||||
if !ok {
|
||||
h.siteCache[r.Call.PC()] = 0
|
||||
h.siteCache[r.PC] = 0
|
||||
}
|
||||
h.lock.Unlock()
|
||||
}
|
||||
if lvl >= r.Lvl {
|
||||
return h.origin.Log(r)
|
||||
if lvl <= r.Level {
|
||||
return h.origin.Handle(context.Background(), r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
// +build !go1.4
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// swapHandler wraps another handler that may be swapped out
|
||||
// dynamically at runtime in a thread-safe fashion.
|
||||
type swapHandler struct {
|
||||
handler unsafe.Pointer
|
||||
}
|
||||
|
||||
func (h *swapHandler) Log(r *Record) error {
|
||||
return h.Get().Log(r)
|
||||
}
|
||||
|
||||
func (h *swapHandler) Get() Handler {
|
||||
return *(*Handler)(atomic.LoadPointer(&h.handler))
|
||||
}
|
||||
|
||||
func (h *swapHandler) Swap(newHandler Handler) {
|
||||
atomic.StorePointer(&h.handler, unsafe.Pointer(&newHandler))
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// +build go1.4
|
||||
|
||||
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.Value
|
||||
}
|
||||
|
||||
func (h *swapHandler) Log(r *Record) error {
|
||||
return (*h.handler.Load().(*Handler)).Log(r)
|
||||
}
|
||||
|
||||
func (h *swapHandler) Swap(newHandler Handler) {
|
||||
h.handler.Store(&newHandler)
|
||||
}
|
||||
|
||||
func (h *swapHandler) Get() Handler {
|
||||
return *h.handler.Load().(*Handler)
|
||||
}
|
||||
316
log/logger.go
316
log/logger.go
|
|
@ -1,240 +1,216 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"log/slog"
|
||||
"math"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/go-stack/stack"
|
||||
)
|
||||
|
||||
const timeKey = "t"
|
||||
const lvlKey = "lvl"
|
||||
const msgKey = "msg"
|
||||
const errorKey = "LOG15_ERROR"
|
||||
|
||||
type Lvl int
|
||||
const errorKey = "LOG_ERROR"
|
||||
|
||||
const (
|
||||
LvlCrit Lvl = iota
|
||||
LvlError
|
||||
LvlWarn
|
||||
LvlInfo
|
||||
LvlDebug
|
||||
LvlTrace
|
||||
legacyLevelCrit = iota
|
||||
legacyLevelError
|
||||
legacyLevelWarn
|
||||
legacyLevelInfo
|
||||
legacyLevelDebug
|
||||
legacyLevelTrace
|
||||
)
|
||||
|
||||
// Aligned returns a 5-character string containing the name of a Lvl.
|
||||
func (l Lvl) AlignedString() string {
|
||||
const (
|
||||
levelMaxVerbosity slog.Level = math.MinInt
|
||||
LevelTrace slog.Level = -8
|
||||
LevelDebug = slog.LevelDebug
|
||||
LevelInfo = slog.LevelInfo
|
||||
LevelWarn = slog.LevelWarn
|
||||
LevelError = slog.LevelError
|
||||
LevelCrit slog.Level = 12
|
||||
|
||||
// for backward-compatibility
|
||||
LvlTrace = LevelTrace
|
||||
LvlInfo = LevelInfo
|
||||
LvlDebug = LevelDebug
|
||||
)
|
||||
|
||||
// FromLegacyLevel converts from old Geth verbosity level constants
|
||||
// to levels defined by slog
|
||||
func FromLegacyLevel(lvl int) slog.Level {
|
||||
switch lvl {
|
||||
case legacyLevelCrit:
|
||||
return LevelCrit
|
||||
case legacyLevelError:
|
||||
return slog.LevelError
|
||||
case legacyLevelWarn:
|
||||
return slog.LevelWarn
|
||||
case legacyLevelInfo:
|
||||
return slog.LevelInfo
|
||||
case legacyLevelDebug:
|
||||
return slog.LevelDebug
|
||||
case legacyLevelTrace:
|
||||
return LevelTrace
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// TODO: should we allow use of custom levels or force them to match existing max/min if they fall outside the range as I am doing here?
|
||||
if lvl > legacyLevelTrace {
|
||||
return LevelTrace
|
||||
}
|
||||
return LevelCrit
|
||||
}
|
||||
|
||||
// LevelAlignedString returns a 5-character string containing the name of a Lvl.
|
||||
func LevelAlignedString(l slog.Level) string {
|
||||
switch l {
|
||||
case LvlTrace:
|
||||
case LevelTrace:
|
||||
return "TRACE"
|
||||
case LvlDebug:
|
||||
case slog.LevelDebug:
|
||||
return "DEBUG"
|
||||
case LvlInfo:
|
||||
case slog.LevelInfo:
|
||||
return "INFO "
|
||||
case LvlWarn:
|
||||
case slog.LevelWarn:
|
||||
return "WARN "
|
||||
case LvlError:
|
||||
case slog.LevelError:
|
||||
return "ERROR"
|
||||
case LvlCrit:
|
||||
case LevelCrit:
|
||||
return "CRIT "
|
||||
default:
|
||||
panic("bad level")
|
||||
return "unknown level"
|
||||
}
|
||||
}
|
||||
|
||||
// Strings returns the name of a Lvl.
|
||||
func (l Lvl) String() string {
|
||||
// LevelString returns a string containing the name of a Lvl.
|
||||
func LevelString(l slog.Level) string {
|
||||
switch l {
|
||||
case LvlTrace:
|
||||
return "trce"
|
||||
case LvlDebug:
|
||||
return "dbug"
|
||||
case LvlInfo:
|
||||
case LevelTrace:
|
||||
return "trace"
|
||||
case slog.LevelDebug:
|
||||
return "debug"
|
||||
case slog.LevelInfo:
|
||||
return "info"
|
||||
case LvlWarn:
|
||||
case slog.LevelWarn:
|
||||
return "warn"
|
||||
case LvlError:
|
||||
return "eror"
|
||||
case LvlCrit:
|
||||
case slog.LevelError:
|
||||
return "error"
|
||||
case LevelCrit:
|
||||
return "crit"
|
||||
default:
|
||||
panic("bad level")
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the appropriate Lvl from a string name.
|
||||
// Useful for parsing command line args and configuration files.
|
||||
func LvlFromString(lvlString string) (Lvl, error) {
|
||||
switch lvlString {
|
||||
case "trace", "trce":
|
||||
return LvlTrace, nil
|
||||
case "debug", "dbug":
|
||||
return LvlDebug, nil
|
||||
case "info":
|
||||
return LvlInfo, nil
|
||||
case "warn":
|
||||
return LvlWarn, nil
|
||||
case "error", "eror":
|
||||
return LvlError, nil
|
||||
case "crit":
|
||||
return LvlCrit, nil
|
||||
default:
|
||||
return LvlDebug, fmt.Errorf("unknown level: %v", lvlString)
|
||||
}
|
||||
}
|
||||
|
||||
// A Record is what a Logger asks its handler to write
|
||||
type Record struct {
|
||||
Time time.Time
|
||||
Lvl Lvl
|
||||
Msg string
|
||||
Ctx []interface{}
|
||||
Call stack.Call
|
||||
KeyNames RecordKeyNames
|
||||
}
|
||||
|
||||
type RecordKeyNames struct {
|
||||
Time string
|
||||
Msg string
|
||||
Lvl string
|
||||
}
|
||||
|
||||
// A Logger writes key/value pairs to a Handler
|
||||
type Logger interface {
|
||||
// New returns a new Logger that has this logger's context plus the given context
|
||||
// With returns a new Logger that has this logger's attributes plus the given attributes
|
||||
With(ctx ...interface{}) Logger
|
||||
|
||||
// New returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'.
|
||||
New(ctx ...interface{}) Logger
|
||||
|
||||
// GetHandler gets the handler associated with the logger.
|
||||
GetHandler() Handler
|
||||
// Log logs a message at the specified level with context key/value pairs
|
||||
Log(level slog.Level, msg string, ctx ...interface{})
|
||||
|
||||
// SetHandler updates the logger to write records to the specified handler.
|
||||
SetHandler(h Handler)
|
||||
|
||||
// Log a message at the given level with context key/value pairs
|
||||
// Trace log a message at the trace level with context key/value pairs
|
||||
Trace(msg string, ctx ...interface{})
|
||||
|
||||
// Debug logs a message at the debug level with context key/value pairs
|
||||
Debug(msg string, ctx ...interface{})
|
||||
|
||||
// Info logs a message at the info level with context key/value pairs
|
||||
Info(msg string, ctx ...interface{})
|
||||
|
||||
// Warn logs a message at the warn level with context key/value pairs
|
||||
Warn(msg string, ctx ...interface{})
|
||||
|
||||
// Error logs a message at the error level with context key/value pairs
|
||||
Error(msg string, ctx ...interface{})
|
||||
|
||||
// Crit logs a message at the crit level with context key/value pairs, and exits
|
||||
Crit(msg string, ctx ...interface{})
|
||||
|
||||
// Write logs a message at the specified level
|
||||
Write(level slog.Level, msg string, attrs ...any)
|
||||
|
||||
// Enabled reports whether l emits log records at the given context and level.
|
||||
Enabled(ctx context.Context, level slog.Level) bool
|
||||
|
||||
// Handler returns the underlying handler of the inner logger.
|
||||
Handler() slog.Handler
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
ctx []interface{}
|
||||
h *swapHandler
|
||||
inner *slog.Logger
|
||||
}
|
||||
|
||||
func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) {
|
||||
l.h.Log(&Record{
|
||||
Time: time.Now(),
|
||||
Lvl: lvl,
|
||||
Msg: msg,
|
||||
Ctx: newContext(l.ctx, ctx),
|
||||
Call: stack.Caller(2),
|
||||
KeyNames: RecordKeyNames{
|
||||
Time: timeKey,
|
||||
Msg: msgKey,
|
||||
Lvl: lvlKey,
|
||||
},
|
||||
})
|
||||
// NewLogger returns a logger with the specified handler set
|
||||
func NewLogger(h slog.Handler) Logger {
|
||||
return &logger{
|
||||
slog.New(h),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logger) Handler() slog.Handler {
|
||||
return l.inner.Handler()
|
||||
}
|
||||
|
||||
// Write logs a message at the specified level.
|
||||
func (l *logger) Write(level slog.Level, msg string, attrs ...any) {
|
||||
if !l.inner.Enabled(context.Background(), level) {
|
||||
return
|
||||
}
|
||||
|
||||
var pcs [1]uintptr
|
||||
runtime.Callers(3, pcs[:])
|
||||
|
||||
if len(attrs)%2 != 0 {
|
||||
attrs = append(attrs, nil, errorKey, "Normalized odd number of arguments by adding nil")
|
||||
}
|
||||
r := slog.NewRecord(time.Now(), level, msg, pcs[0])
|
||||
r.Add(attrs...)
|
||||
l.inner.Handler().Handle(context.Background(), r)
|
||||
}
|
||||
|
||||
func (l *logger) Log(level slog.Level, msg string, attrs ...any) {
|
||||
l.Write(level, msg, attrs...)
|
||||
}
|
||||
|
||||
func (l *logger) With(ctx ...interface{}) Logger {
|
||||
return &logger{l.inner.With(ctx...)}
|
||||
}
|
||||
|
||||
func (l *logger) New(ctx ...interface{}) Logger {
|
||||
child := &logger{newContext(l.ctx, ctx), new(swapHandler)}
|
||||
child.SetHandler(l.h)
|
||||
return child
|
||||
return l.With(ctx...)
|
||||
}
|
||||
|
||||
func newContext(prefix []interface{}, suffix []interface{}) []interface{} {
|
||||
normalizedSuffix := normalize(suffix)
|
||||
newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix))
|
||||
n := copy(newCtx, prefix)
|
||||
copy(newCtx[n:], normalizedSuffix)
|
||||
return newCtx
|
||||
// Enabled reports whether l emits log records at the given context and level.
|
||||
func (l *logger) Enabled(ctx context.Context, level slog.Level) bool {
|
||||
return l.inner.Enabled(ctx, level)
|
||||
}
|
||||
|
||||
func (l *logger) Trace(msg string, ctx ...interface{}) {
|
||||
l.write(msg, LvlTrace, ctx)
|
||||
l.Write(LevelTrace, msg, ctx...)
|
||||
}
|
||||
|
||||
func (l *logger) Debug(msg string, ctx ...interface{}) {
|
||||
l.write(msg, LvlDebug, ctx)
|
||||
l.Write(slog.LevelDebug, msg, ctx...)
|
||||
}
|
||||
|
||||
func (l *logger) Info(msg string, ctx ...interface{}) {
|
||||
l.write(msg, LvlInfo, ctx)
|
||||
l.Write(slog.LevelInfo, msg, ctx...)
|
||||
}
|
||||
|
||||
func (l *logger) Warn(msg string, ctx ...interface{}) {
|
||||
l.write(msg, LvlWarn, ctx)
|
||||
func (l *logger) Warn(msg string, ctx ...any) {
|
||||
l.Write(slog.LevelWarn, msg, ctx...)
|
||||
}
|
||||
|
||||
func (l *logger) Error(msg string, ctx ...interface{}) {
|
||||
l.write(msg, LvlError, ctx)
|
||||
l.Write(slog.LevelError, msg, ctx...)
|
||||
}
|
||||
|
||||
func (l *logger) Crit(msg string, ctx ...interface{}) {
|
||||
l.write(msg, LvlCrit, ctx)
|
||||
l.Write(LevelCrit, msg, ctx...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (l *logger) GetHandler() Handler {
|
||||
return l.h.Get()
|
||||
}
|
||||
|
||||
func (l *logger) SetHandler(h Handler) {
|
||||
l.h.Swap(h)
|
||||
}
|
||||
|
||||
func normalize(ctx []interface{}) []interface{} {
|
||||
// if the caller passed a Ctx object, then expand it
|
||||
if len(ctx) == 1 {
|
||||
if ctxMap, ok := ctx[0].(Ctx); ok {
|
||||
ctx = ctxMap.toArray()
|
||||
}
|
||||
}
|
||||
|
||||
// ctx needs to be even because it's a series of key/value pairs
|
||||
// no one wants to check for errors on logging functions,
|
||||
// so instead of erroring on bad input, we'll just make sure
|
||||
// that things are the right length and users can fix bugs
|
||||
// when they see the output looks wrong
|
||||
if len(ctx)%2 != 0 {
|
||||
ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil")
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Lazy allows you to defer calculation of a logged value that is expensive
|
||||
// to compute until it is certain that it must be evaluated with the given filters.
|
||||
//
|
||||
// Lazy may also be used in conjunction with a Logger's New() function
|
||||
// to generate a child logger which always reports the current value of changing
|
||||
// state.
|
||||
//
|
||||
// You may wrap any function which takes no arguments to Lazy. It may return any
|
||||
// number of values of any type.
|
||||
type Lazy struct {
|
||||
Fn interface{}
|
||||
}
|
||||
|
||||
// Ctx is a map of key/value pairs to pass as context to a log function
|
||||
// Use this only if you really need greater safety around the arguments you pass
|
||||
// to the logging functions.
|
||||
type Ctx map[string]interface{}
|
||||
|
||||
func (c Ctx) toArray() []interface{} {
|
||||
arr := make([]interface{}, len(c)*2)
|
||||
|
||||
i := 0
|
||||
for k, v := range c {
|
||||
arr[i] = k
|
||||
arr[i+1] = v
|
||||
i += 2
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
|
|
|||
191
log/logger_test.go
Normal file
191
log/logger_test.go
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// TestLoggingWithVmodule checks that vmodule works.
|
||||
func TestLoggingWithVmodule(t *testing.T) {
|
||||
out := new(bytes.Buffer)
|
||||
glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false))
|
||||
glog.Verbosity(LevelCrit)
|
||||
logger := NewLogger(glog)
|
||||
logger.Warn("This should not be seen", "ignored", "true")
|
||||
glog.Vmodule("logger_test.go=5")
|
||||
logger.Trace("a message", "foo", "bar")
|
||||
have := out.String()
|
||||
// The timestamp is locale-dependent, so we want to trim that off
|
||||
// "INFO [01-01|00:00:00.000] a message ..." -> "a message..."
|
||||
have = strings.Split(have, "]")[1]
|
||||
want := " a message foo=bar\n"
|
||||
if have != want {
|
||||
t.Errorf("\nhave: %q\nwant: %q\n", have, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTerminalHandlerWithAttrs(t *testing.T) {
|
||||
out := new(bytes.Buffer)
|
||||
glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false).WithAttrs([]slog.Attr{slog.String("baz", "bat")}))
|
||||
glog.Verbosity(LevelTrace)
|
||||
logger := NewLogger(glog)
|
||||
logger.Trace("a message", "foo", "bar")
|
||||
have := out.String()
|
||||
// The timestamp is locale-dependent, so we want to trim that off
|
||||
// "INFO [01-01|00:00:00.000] a message ..." -> "a message..."
|
||||
have = strings.Split(have, "]")[1]
|
||||
want := " a message baz=bat foo=bar\n"
|
||||
if have != want {
|
||||
t.Errorf("\nhave: %q\nwant: %q\n", have, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the default json handler outputs debug log lines
|
||||
func TestJSONHandler(t *testing.T) {
|
||||
out := new(bytes.Buffer)
|
||||
handler := JSONHandler(out)
|
||||
logger := slog.New(handler)
|
||||
logger.Debug("hi there")
|
||||
if len(out.String()) == 0 {
|
||||
t.Error("expected non-empty debug log output from default JSON Handler")
|
||||
}
|
||||
|
||||
out.Reset()
|
||||
handler = JSONHandlerWithLevel(out, slog.LevelInfo)
|
||||
logger = slog.New(handler)
|
||||
logger.Debug("hi there")
|
||||
if len(out.String()) != 0 {
|
||||
t.Errorf("expected empty debug log output, but got: %v", out.String())
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTraceLogging(b *testing.B) {
|
||||
SetDefault(NewLogger(NewTerminalHandler(io.Discard, true)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Trace("a message", "v", i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTerminalHandler(b *testing.B) {
|
||||
l := NewLogger(NewTerminalHandler(io.Discard, false))
|
||||
benchmarkLogger(b, l)
|
||||
}
|
||||
func BenchmarkLogfmtHandler(b *testing.B) {
|
||||
l := NewLogger(LogfmtHandler(io.Discard))
|
||||
benchmarkLogger(b, l)
|
||||
}
|
||||
|
||||
func BenchmarkJSONHandler(b *testing.B) {
|
||||
l := NewLogger(JSONHandler(io.Discard))
|
||||
benchmarkLogger(b, l)
|
||||
}
|
||||
|
||||
func benchmarkLogger(b *testing.B, l Logger) {
|
||||
var (
|
||||
bb = make([]byte, 10)
|
||||
tt = time.Now()
|
||||
bigint = big.NewInt(100)
|
||||
nilbig *big.Int
|
||||
err = errors.New("oh nooes it's crap")
|
||||
)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.Info("This is a message",
|
||||
"foo", int16(i),
|
||||
"bytes", bb,
|
||||
"bonk", "a string with text",
|
||||
"time", tt,
|
||||
"bigint", bigint,
|
||||
"nilbig", nilbig,
|
||||
"err", err)
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func TestLoggerOutput(t *testing.T) {
|
||||
type custom struct {
|
||||
A string
|
||||
B int8
|
||||
}
|
||||
var (
|
||||
customA = custom{"Foo", 12}
|
||||
customB = custom{"Foo\nLinebreak", 122}
|
||||
bb = make([]byte, 10)
|
||||
tt = time.Time{}
|
||||
bigint = big.NewInt(100)
|
||||
nilbig *big.Int
|
||||
err = errors.New("oh nooes it's crap")
|
||||
smallUint = uint256.NewInt(500_000)
|
||||
bigUint = &uint256.Int{0xff, 0xff, 0xff, 0xff}
|
||||
)
|
||||
|
||||
out := new(bytes.Buffer)
|
||||
glogHandler := NewGlogHandler(NewTerminalHandler(out, false))
|
||||
glogHandler.Verbosity(LevelInfo)
|
||||
NewLogger(glogHandler).Info("This is a message",
|
||||
"foo", int16(123),
|
||||
"bytes", bb,
|
||||
"bonk", "a string with text",
|
||||
"time", tt,
|
||||
"bigint", bigint,
|
||||
"nilbig", nilbig,
|
||||
"err", err,
|
||||
"struct", customA,
|
||||
"struct", customB,
|
||||
"ptrstruct", &customA,
|
||||
"smalluint", smallUint,
|
||||
"bigUint", bigUint)
|
||||
|
||||
have := out.String()
|
||||
t.Logf("output %v", out.String())
|
||||
want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig=<nil> err="oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095
|
||||
`
|
||||
if !bytes.Equal([]byte(have)[25:], []byte(want)[25:]) {
|
||||
t.Errorf("Error\nhave: %q\nwant: %q", have, want)
|
||||
}
|
||||
}
|
||||
|
||||
const termTimeFormat = "01-02|15:04:05.000"
|
||||
|
||||
func BenchmarkAppendFormat(b *testing.B) {
|
||||
var now = time.Now()
|
||||
b.Run("fmt time.Format", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
fmt.Fprintf(io.Discard, "%s", now.Format(termTimeFormat))
|
||||
}
|
||||
})
|
||||
b.Run("time.AppendFormat", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
now.AppendFormat(nil, termTimeFormat)
|
||||
}
|
||||
})
|
||||
var buf = new(bytes.Buffer)
|
||||
b.Run("time.Custom", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
writeTimeTermFormat(buf, now)
|
||||
buf.Reset()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTermTimeFormat(t *testing.T) {
|
||||
var now = time.Now()
|
||||
want := now.AppendFormat(nil, termTimeFormat)
|
||||
var b = new(bytes.Buffer)
|
||||
writeTimeTermFormat(b, now)
|
||||
have := b.Bytes()
|
||||
if !bytes.Equal(have, want) {
|
||||
t.Errorf("have != want\nhave: %q\nwant: %q\n", have, want)
|
||||
}
|
||||
}
|
||||
149
log/root.go
149
log/root.go
|
|
@ -1,61 +1,120 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
root = &logger{[]interface{}{}, new(swapHandler)}
|
||||
StdoutHandler = StreamHandler(os.Stdout, LogfmtFormat())
|
||||
StderrHandler = StreamHandler(os.Stderr, LogfmtFormat())
|
||||
)
|
||||
var root atomic.Value
|
||||
|
||||
func init() {
|
||||
root.SetHandler(DiscardHandler())
|
||||
root.Store(&logger{slog.New(DiscardHandler())})
|
||||
}
|
||||
|
||||
// SetDefault sets the default global logger
|
||||
func SetDefault(l Logger) {
|
||||
root.Store(l)
|
||||
if lg, ok := l.(*logger); ok {
|
||||
slog.SetDefault(lg.inner)
|
||||
}
|
||||
}
|
||||
|
||||
// Root returns the root logger
|
||||
func Root() Logger {
|
||||
return root.Load().(Logger)
|
||||
}
|
||||
|
||||
// The following functions bypass the exported logger methods (logger.Debug,
|
||||
// etc.) to keep the call depth the same for all paths to logger.Write so
|
||||
// runtime.Caller(2) always refers to the call site in client code.
|
||||
|
||||
// Trace is a convenient alias for Root().Trace
|
||||
//
|
||||
// Log a message at the trace level with context key/value pairs
|
||||
//
|
||||
// # Usage
|
||||
//
|
||||
// log.Trace("msg")
|
||||
// log.Trace("msg", "key1", val1)
|
||||
// log.Trace("msg", "key1", val1, "key2", val2)
|
||||
func Trace(msg string, ctx ...interface{}) {
|
||||
Root().Write(LevelTrace, msg, ctx...)
|
||||
}
|
||||
|
||||
// Debug is a convenient alias for Root().Debug
|
||||
//
|
||||
// Log a message at the debug level with context key/value pairs
|
||||
//
|
||||
// # Usage Examples
|
||||
//
|
||||
// log.Debug("msg")
|
||||
// log.Debug("msg", "key1", val1)
|
||||
// log.Debug("msg", "key1", val1, "key2", val2)
|
||||
func Debug(msg string, ctx ...interface{}) {
|
||||
Root().Write(slog.LevelDebug, msg, ctx...)
|
||||
}
|
||||
|
||||
// Info is a convenient alias for Root().Info
|
||||
//
|
||||
// Log a message at the info level with context key/value pairs
|
||||
//
|
||||
// # Usage Examples
|
||||
//
|
||||
// log.Info("msg")
|
||||
// log.Info("msg", "key1", val1)
|
||||
// log.Info("msg", "key1", val1, "key2", val2)
|
||||
func Info(msg string, ctx ...interface{}) {
|
||||
Root().Write(slog.LevelInfo, msg, ctx...)
|
||||
}
|
||||
|
||||
// Warn is a convenient alias for Root().Warn
|
||||
//
|
||||
// Log a message at the warn level with context key/value pairs
|
||||
//
|
||||
// # Usage Examples
|
||||
//
|
||||
// log.Warn("msg")
|
||||
// log.Warn("msg", "key1", val1)
|
||||
// log.Warn("msg", "key1", val1, "key2", val2)
|
||||
func Warn(msg string, ctx ...interface{}) {
|
||||
Root().Write(slog.LevelWarn, msg, ctx...)
|
||||
}
|
||||
|
||||
// Error is a convenient alias for Root().Error
|
||||
//
|
||||
// Log a message at the error level with context key/value pairs
|
||||
//
|
||||
// # Usage Examples
|
||||
//
|
||||
// log.Error("msg")
|
||||
// log.Error("msg", "key1", val1)
|
||||
// log.Error("msg", "key1", val1, "key2", val2)
|
||||
func Error(msg string, ctx ...interface{}) {
|
||||
Root().Write(slog.LevelError, msg, ctx...)
|
||||
}
|
||||
|
||||
// Crit is a convenient alias for Root().Crit
|
||||
//
|
||||
// Log a message at the crit level with context key/value pairs, and then exit.
|
||||
//
|
||||
// # Usage Examples
|
||||
//
|
||||
// log.Crit("msg")
|
||||
// log.Crit("msg", "key1", val1)
|
||||
// log.Crit("msg", "key1", val1, "key2", val2)
|
||||
func Crit(msg string, ctx ...interface{}) {
|
||||
Root().Write(LevelCrit, msg, ctx...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// New returns a new logger with the given context.
|
||||
// New is a convenient alias for Root().New
|
||||
func New(ctx ...interface{}) Logger {
|
||||
return root.New(ctx...)
|
||||
return Root().With(ctx...)
|
||||
}
|
||||
|
||||
// Root returns the root logger
|
||||
func Root() Logger {
|
||||
return root
|
||||
}
|
||||
|
||||
// The following functions bypass the exported logger methods (logger.Debug,
|
||||
// etc.) to keep the call depth the same for all paths to logger.write so
|
||||
// runtime.Caller(2) always refers to the call site in client code.
|
||||
|
||||
// Trace is a convenient alias for Root().Trace
|
||||
func Trace(msg string, ctx ...interface{}) {
|
||||
root.write(msg, LvlTrace, ctx)
|
||||
}
|
||||
|
||||
// Debug is a convenient alias for Root().Debug
|
||||
func Debug(msg string, ctx ...interface{}) {
|
||||
root.write(msg, LvlDebug, ctx)
|
||||
}
|
||||
|
||||
// Info is a convenient alias for Root().Info
|
||||
func Info(msg string, ctx ...interface{}) {
|
||||
root.write(msg, LvlInfo, ctx)
|
||||
}
|
||||
|
||||
// Warn is a convenient alias for Root().Warn
|
||||
func Warn(msg string, ctx ...interface{}) {
|
||||
root.write(msg, LvlWarn, ctx)
|
||||
}
|
||||
|
||||
// Error is a convenient alias for Root().Error
|
||||
func Error(msg string, ctx ...interface{}) {
|
||||
root.write(msg, LvlError, ctx)
|
||||
}
|
||||
|
||||
// Crit is a convenient alias for Root().Crit
|
||||
func Crit(msg string, ctx ...interface{}) {
|
||||
root.write(msg, LvlCrit, ctx)
|
||||
os.Exit(1)
|
||||
func Enabled(level slog.Level) bool {
|
||||
return Root().Enabled(context.Background(), level)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
// +build !windows,!plan9
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"log/syslog"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SyslogHandler opens a connection to the system syslog daemon by calling
|
||||
// syslog.New and writes all records to it.
|
||||
func SyslogHandler(priority syslog.Priority, tag string, fmtr Format) (Handler, error) {
|
||||
wr, err := syslog.New(priority, tag)
|
||||
return sharedSyslog(fmtr, wr, err)
|
||||
}
|
||||
|
||||
// SyslogNetHandler opens a connection to a log daemon over the network and writes
|
||||
// all log records to it.
|
||||
func SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) (Handler, error) {
|
||||
wr, err := syslog.Dial(net, addr, priority, tag)
|
||||
return sharedSyslog(fmtr, wr, err)
|
||||
}
|
||||
|
||||
func sharedSyslog(fmtr Format, sysWr *syslog.Writer, err error) (Handler, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h := FuncHandler(func(r *Record) error {
|
||||
var syslogFn = sysWr.Info
|
||||
switch r.Lvl {
|
||||
case LvlCrit:
|
||||
syslogFn = sysWr.Crit
|
||||
case LvlError:
|
||||
syslogFn = sysWr.Err
|
||||
case LvlWarn:
|
||||
syslogFn = sysWr.Warning
|
||||
case LvlInfo:
|
||||
syslogFn = sysWr.Info
|
||||
case LvlDebug:
|
||||
syslogFn = sysWr.Debug
|
||||
case LvlTrace:
|
||||
syslogFn = func(m string) error { return nil } // There's no syslog level for trace
|
||||
}
|
||||
|
||||
s := strings.TrimSpace(string(fmtr.Format(r)))
|
||||
return syslogFn(s)
|
||||
})
|
||||
return LazyHandler(&closingHandler{sysWr, h}), nil
|
||||
}
|
||||
|
||||
func (m muster) SyslogHandler(priority syslog.Priority, tag string, fmtr Format) Handler {
|
||||
return must(SyslogHandler(priority, tag, fmtr))
|
||||
}
|
||||
|
||||
func (m muster) SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) Handler {
|
||||
return must(SyslogNetHandler(net, addr, priority, tag, fmtr))
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Simon Eskildsen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
// Based on ssh/terminal:
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build appengine
|
||||
|
||||
package term
|
||||
|
||||
// IsTty always returns false on AppEngine.
|
||||
func IsTty(fd uintptr) bool {
|
||||
return false
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
// Based on ssh/terminal:
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// +build !appengine
|
||||
|
||||
package term
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
|
||||
type Termios syscall.Termios
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package term
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
|
||||
// Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]uint8
|
||||
Ispeed uint32
|
||||
Ospeed uint32
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// Based on ssh/terminal:
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package term
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TCGETS
|
||||
|
||||
type Termios syscall.Termios
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package term
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
|
||||
type Termios syscall.Termios
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
// Based on ssh/terminal:
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux,!appengine darwin freebsd openbsd netbsd
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IsTty returns true if the given file descriptor is a terminal.
|
||||
func IsTty(fd uintptr) bool {
|
||||
var termios Termios
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package term
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
|
||||
type Termios syscall.Termios
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package term
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// IsTty returns true if the given file descriptor is a terminal.
|
||||
func IsTty(fd uintptr) bool {
|
||||
_, err := unix.IoctlGetTermios(int(fd), unix.TCGETA)
|
||||
return err == nil
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
// Based on ssh/terminal:
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
var (
|
||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||
)
|
||||
|
||||
// IsTty returns true if the given file descriptor is a terminal.
|
||||
func IsTty(fd uintptr) bool {
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ import (
|
|||
|
||||
func init() {
|
||||
// Initialize the logger
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, false)))
|
||||
|
||||
// Initialize the goroutine count
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
|
|
|||
|
|
@ -24,5 +24,5 @@ import (
|
|||
|
||||
// SetVerbosity sets the global verbosity level (between 0 and 6 - see logger/verbosity.go).
|
||||
func SetVerbosity(level int) {
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(level), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.FromLegacyLevel(level), false)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
package discover
|
||||
|
||||
import (
|
||||
"context"
|
||||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
|
@ -72,7 +73,10 @@ type Table struct {
|
|||
rand *mrand.Rand // source of randomness, periodically reseeded
|
||||
ips netutil.DistinctNetSet
|
||||
|
||||
db *nodeDB // database of known nodes
|
||||
db *nodeDB // database of known nodes
|
||||
log log.Logger
|
||||
|
||||
// loop channels
|
||||
refreshReq chan chan struct{}
|
||||
initDone chan struct{}
|
||||
closeReq chan struct{}
|
||||
|
|
@ -118,9 +122,11 @@ func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tab := &Table{
|
||||
net: t,
|
||||
db: db,
|
||||
log: log.Root(),
|
||||
self: NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)),
|
||||
bonding: make(map[NodeID]*bondproc),
|
||||
bondslots: make(chan struct{}, maxBondingPingPongs),
|
||||
|
|
@ -322,10 +328,10 @@ func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node {
|
|||
// Bump the failure counter to detect and evacuate non-bonded entries
|
||||
fails := tab.db.findFails(n.ID) + 1
|
||||
tab.db.updateFindFails(n.ID, fails)
|
||||
log.Trace("Bumping findnode failure counter", "id", n.ID, "failcount", fails)
|
||||
tab.log.Trace("Bumping findnode failure counter", "id", n.ID, "failcount", fails)
|
||||
|
||||
if fails >= maxFindnodeFailures {
|
||||
log.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails)
|
||||
tab.log.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails)
|
||||
tab.delete(n)
|
||||
}
|
||||
}
|
||||
|
|
@ -455,8 +461,10 @@ func (tab *Table) loadSeedNodes(bond bool) {
|
|||
}
|
||||
for i := range seeds {
|
||||
seed := seeds[i]
|
||||
age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.bondTime(seed.ID)) }}
|
||||
log.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age)
|
||||
if tab.log.Enabled(context.Background(), log.LevelTrace) {
|
||||
age := time.Since(tab.db.bondTime(seed.ID))
|
||||
tab.log.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age)
|
||||
}
|
||||
tab.add(seed)
|
||||
}
|
||||
}
|
||||
|
|
@ -480,16 +488,16 @@ func (tab *Table) doRevalidate(done chan<- struct{}) {
|
|||
b := tab.buckets[bi]
|
||||
if err == nil {
|
||||
// The node responded, move it to the front.
|
||||
log.Debug("Revalidated node", "b", bi, "id", last.ID)
|
||||
tab.log.Debug("Revalidated node", "b", bi, "id", last.ID)
|
||||
b.bump(last)
|
||||
return
|
||||
}
|
||||
// No reply received, pick a replacement or delete the node if there aren't
|
||||
// any replacements.
|
||||
if r := tab.replace(b, last); r != nil {
|
||||
log.Debug("Replaced dead node", "b", bi, "id", last.ID, "ip", last.IP, "r", r.ID, "rip", r.IP)
|
||||
tab.log.Debug("Replaced dead node", "b", bi, "id", last.ID, "ip", last.IP, "r", r.ID, "rip", r.IP)
|
||||
} else {
|
||||
log.Debug("Removed dead node", "b", bi, "id", last.ID, "ip", last.IP)
|
||||
tab.log.Debug("Removed dead node", "b", bi, "id", last.ID, "ip", last.IP)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -599,7 +607,7 @@ func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16
|
|||
age := time.Since(tab.db.bondTime(id))
|
||||
var result error
|
||||
if fails > 0 || age > nodeDBNodeExpiration {
|
||||
log.Trace("Starting bonding ping/pong", "id", id, "known", node != nil, "failcount", fails, "age", age)
|
||||
tab.log.Trace("Starting bonding ping/pong", "id", id, "known", node != nil, "failcount", fails, "age", age)
|
||||
|
||||
tab.bondmu.Lock()
|
||||
w := tab.bonding[id]
|
||||
|
|
@ -724,11 +732,11 @@ func (tab *Table) addIP(b *bucket, ip net.IP) bool {
|
|||
return true
|
||||
}
|
||||
if !tab.ips.Add(ip) {
|
||||
log.Debug("IP exceeds table limit", "ip", ip)
|
||||
tab.log.Debug("IP exceeds table limit", "ip", ip)
|
||||
return false
|
||||
}
|
||||
if !b.ips.Add(ip) {
|
||||
log.Debug("IP exceeds bucket limit", "ip", ip)
|
||||
tab.log.Debug("IP exceeds bucket limit", "ip", ip)
|
||||
tab.ips.Remove(ip)
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -228,6 +228,9 @@ type Config struct {
|
|||
NetRestrict *netutil.Netlist // network whitelist
|
||||
Bootnodes []*Node // list of bootstrap nodes
|
||||
Unhandled chan<- ReadPacket // unhandled packets are sent on this channel
|
||||
|
||||
// The options below are useful in very specific cases, like in unit tests.
|
||||
Log log.Logger // if set, log messages go here
|
||||
}
|
||||
|
||||
// ListenUDP returns a new table that listens for UDP packets on laddr.
|
||||
|
|
|
|||
|
|
@ -420,7 +420,6 @@ loop:
|
|||
|
||||
// Ingress packet handling.
|
||||
case pkt := <-net.read:
|
||||
//fmt.Println("read", pkt.ev)
|
||||
log.Trace("<-net.read")
|
||||
n := net.internNode(&pkt)
|
||||
prestate := n.state
|
||||
|
|
@ -428,10 +427,10 @@ loop:
|
|||
if err := net.handle(n, pkt.ev, &pkt); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
log.Trace("", "msg", log.Lazy{Fn: func() string {
|
||||
return fmt.Sprintf("<<< (%d) %v from %x@%v: %v -> %v (%v)",
|
||||
net.tab.count, pkt.ev, pkt.remoteID[:8], pkt.remoteAddr, prestate, n.state, status)
|
||||
}})
|
||||
if log.Enabled(log.LevelTrace) {
|
||||
msg := fmt.Sprintf("<<< (%d) %v from %x@%v: %v -> %v (%v)", net.tab.count, pkt.ev, pkt.remoteID[:8], pkt.remoteAddr, prestate, n.state, status)
|
||||
log.Trace("", "msg", msg)
|
||||
}
|
||||
// TODO: persist state if n.state goes >= known, delete if it goes <= known
|
||||
|
||||
// State transition timeouts.
|
||||
|
|
@ -447,10 +446,10 @@ loop:
|
|||
if err := net.handle(timeout.node, timeout.ev, nil); err != nil {
|
||||
status = err.Error()
|
||||
}
|
||||
log.Trace("", "msg", log.Lazy{Fn: func() string {
|
||||
return fmt.Sprintf("--- (%d) %v for %x@%v: %v -> %v (%v)",
|
||||
net.tab.count, timeout.ev, timeout.node.ID[:8], timeout.node.addr(), prestate, timeout.node.state, status)
|
||||
}})
|
||||
if log.Enabled(log.LevelTrace) {
|
||||
msg := fmt.Sprintf("--- (%d) %v for %x@%v: %v -> %v (%v)", net.tab.count, timeout.ev, timeout.node.ID[:8], timeout.node.addr(), prestate, timeout.node.state, status)
|
||||
log.Trace("", "msg", msg)
|
||||
}
|
||||
|
||||
// Querying.
|
||||
case q := <-net.queryReq:
|
||||
|
|
@ -683,15 +682,15 @@ func (net *Network) refresh(done chan<- struct{}) {
|
|||
return
|
||||
}
|
||||
for _, n := range seeds {
|
||||
log.Debug("", "msg", log.Lazy{Fn: func() string {
|
||||
if log.Enabled(log.LevelDebug) {
|
||||
var age string
|
||||
if net.db != nil {
|
||||
age = time.Since(net.db.lastPong(n.ID)).String()
|
||||
} else {
|
||||
age = "unknown"
|
||||
}
|
||||
return fmt.Sprintf("seed node (age %s): %v", age, n)
|
||||
}})
|
||||
log.Debug("", "msg", fmt.Sprintf("seed node (age %s): %v", age, n))
|
||||
}
|
||||
n = net.internNodeFromDB(n)
|
||||
if n.state == unknown {
|
||||
net.transition(n, verifyinit)
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import (
|
|||
)
|
||||
|
||||
func init() {
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
// log.SetDefault(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
}
|
||||
|
||||
type testTransport struct {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
|
@ -155,8 +156,7 @@ func (n *ExecNode) Client() (*rpc.Client, error) {
|
|||
var wsAddrPattern = regexp.MustCompile(`ws://[\d.:]+`)
|
||||
|
||||
// Start exec's the node passing the ID and service as command line arguments
|
||||
// and the node config encoded as JSON in the _P2P_NODE_CONFIG environment
|
||||
// variable
|
||||
// and the node config encoded as JSON in an environment variable.
|
||||
func (n *ExecNode) Start(snapshots map[string][]byte) (err error) {
|
||||
if n.Cmd != nil {
|
||||
return errors.New("already started")
|
||||
|
|
@ -189,7 +189,7 @@ func (n *ExecNode) Start(snapshots map[string][]byte) (err error) {
|
|||
cmd := n.newCmd()
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = stderr
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("_P2P_NODE_CONFIG=%s", confData))
|
||||
cmd.Env = append(os.Environ(), envNodeConfig+"="+string(confData))
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("error starting node: %s", err)
|
||||
}
|
||||
|
|
@ -344,25 +344,58 @@ type execNodeConfig struct {
|
|||
PeerAddrs map[string]string `json:"peer_addrs,omitempty"`
|
||||
}
|
||||
|
||||
// execP2PNode starts a devp2p node when the current binary is executed with
|
||||
func initLogging() {
|
||||
// Initialize the logging by default first.
|
||||
var innerHandler slog.Handler
|
||||
innerHandler = slog.NewTextHandler(os.Stderr, nil)
|
||||
glogger := log.NewGlogHandler(innerHandler)
|
||||
glogger.Verbosity(log.LevelInfo)
|
||||
log.SetDefault(log.NewLogger(glogger))
|
||||
|
||||
confEnv := os.Getenv(envNodeConfig)
|
||||
if confEnv == "" {
|
||||
return
|
||||
}
|
||||
var conf execNodeConfig
|
||||
if err := json.Unmarshal([]byte(confEnv), &conf); err != nil {
|
||||
return
|
||||
}
|
||||
var writer = os.Stderr
|
||||
if conf.Node.LogFile != "" {
|
||||
logWriter, err := os.Create(conf.Node.LogFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
writer = logWriter
|
||||
}
|
||||
var verbosity = log.LevelInfo
|
||||
if conf.Node.LogVerbosity <= log.LevelTrace && conf.Node.LogVerbosity >= log.LevelCrit {
|
||||
verbosity = log.FromLegacyLevel(int(conf.Node.LogVerbosity))
|
||||
}
|
||||
// Reinitialize the logger
|
||||
innerHandler = log.NewTerminalHandler(writer, true)
|
||||
glogger = log.NewGlogHandler(innerHandler)
|
||||
glogger.Verbosity(verbosity)
|
||||
log.SetDefault(log.NewLogger(glogger))
|
||||
}
|
||||
|
||||
// execP2PNode starts a simulation node when the current binary is executed with
|
||||
// argv[0] being "p2p-node", reading the service / ID from argv[1] / argv[2]
|
||||
// and the node config from the _P2P_NODE_CONFIG environment variable
|
||||
// and the node config from an environment variable.
|
||||
func execP2PNode() {
|
||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.LogfmtFormat()))
|
||||
glogger.Verbosity(log.LvlInfo)
|
||||
log.Root().SetHandler(glogger)
|
||||
initLogging()
|
||||
|
||||
// read the services from argv
|
||||
serviceNames := strings.Split(os.Args[1], ",")
|
||||
|
||||
// decode the config
|
||||
confEnv := os.Getenv("_P2P_NODE_CONFIG")
|
||||
confEnv := os.Getenv(envNodeConfig)
|
||||
if confEnv == "" {
|
||||
log.Crit("missing _P2P_NODE_CONFIG")
|
||||
log.Crit("missing " + envNodeConfig)
|
||||
}
|
||||
var conf execNodeConfig
|
||||
if err := json.Unmarshal([]byte(confEnv), &conf); err != nil {
|
||||
log.Crit("error decoding _P2P_NODE_CONFIG", "err", err)
|
||||
log.Crit("error decoding "+envNodeConfig, "err", err)
|
||||
}
|
||||
conf.Stack.P2P.PrivateKey = conf.Node.PrivateKey
|
||||
conf.Stack.Logger = log.New("node.id", conf.Node.ID.String())
|
||||
|
|
@ -477,6 +510,10 @@ func (s *snapshotService) Stop() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
envNodeConfig = "_P2P_NODE_CONFIG"
|
||||
)
|
||||
|
||||
// SnapshotAPI provides an RPC method to create snapshots of services
|
||||
type SnapshotAPI struct {
|
||||
services map[string]node.Service
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
|
|
@ -38,7 +39,6 @@ import (
|
|||
// * SimNode - An in-memory node
|
||||
// * ExecNode - A child process node
|
||||
// * DockerNode - A Docker container node
|
||||
//
|
||||
type Node interface {
|
||||
// Addr returns the node's address (e.g. an Enode URL)
|
||||
Addr() []byte
|
||||
|
|
@ -97,24 +97,39 @@ type NodeConfig struct {
|
|||
|
||||
// function to sanction or prevent suggesting a peer
|
||||
Reachable func(id discover.NodeID) bool
|
||||
|
||||
// LogFile is the log file name of the p2p node at runtime.
|
||||
//
|
||||
// The default value is empty so that the default log writer
|
||||
// is the system standard output.
|
||||
LogFile string
|
||||
|
||||
// LogVerbosity is the log verbosity of the p2p node at runtime.
|
||||
//
|
||||
// The default verbosity is INFO.
|
||||
LogVerbosity slog.Level
|
||||
}
|
||||
|
||||
// nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding
|
||||
// all fields as strings
|
||||
type nodeConfigJSON struct {
|
||||
ID string `json:"id"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
Name string `json:"name"`
|
||||
Services []string `json:"services"`
|
||||
ID string `json:"id"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
Name string `json:"name"`
|
||||
Services []string `json:"services"`
|
||||
LogFile string `json:"logfile"`
|
||||
LogVerbosity int `json:"log_verbosity"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface by encoding the config
|
||||
// fields as strings
|
||||
func (n *NodeConfig) MarshalJSON() ([]byte, error) {
|
||||
confJSON := nodeConfigJSON{
|
||||
ID: n.ID.String(),
|
||||
Name: n.Name,
|
||||
Services: n.Services,
|
||||
ID: n.ID.String(),
|
||||
Name: n.Name,
|
||||
Services: n.Services,
|
||||
LogFile: n.LogFile,
|
||||
LogVerbosity: int(n.LogVerbosity),
|
||||
}
|
||||
if n.PrivateKey != nil {
|
||||
confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey))
|
||||
|
|
@ -152,6 +167,8 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error {
|
|||
|
||||
n.Name = confJSON.Name
|
||||
n.Services = confJSON.Services
|
||||
n.LogFile = confJSON.LogFile
|
||||
n.LogVerbosity = slog.Level(confJSON.LogVerbosity)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func main() {
|
|||
flag.Parse()
|
||||
|
||||
// set the log level to Trace
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, false)))
|
||||
|
||||
// register a single ping-pong service
|
||||
services := map[string]adapters.ServiceFunc{
|
||||
|
|
|
|||
Loading…
Reference in a new issue