all: replace log15 with slog (#28187)

This commit is contained in:
Daniel Liu 2024-11-15 10:02:42 +08:00
parent b04ce3213a
commit ec4ca1ed6a
34 changed files with 1035 additions and 1747 deletions

View file

@ -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)

View file

@ -136,6 +136,8 @@ var (
utils.GpoIgnoreGasPriceFlag,
//utils.ExtraDataFlag,
configFileFlag,
utils.LogDebugFlag,
utils.LogBacktraceAtFlag,
utils.AnnounceTxsFlag,
utils.StoreRewardFlag,
utils.RollbackFlag,

View file

@ -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 {

View file

@ -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() {

View file

@ -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()

View file

@ -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

View file

@ -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)

View file

@ -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")

View file

@ -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) {

View file

@ -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{

View file

@ -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
View file

@ -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
View file

@ -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=

View file

@ -35,6 +35,7 @@ import (
"time"
"github.com/XinFinOrg/XDPoSChain/log"
"golang.org/x/exp/slog"
)
// Handler is the global debugging handler.
@ -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.

View file

@ -22,35 +22,70 @@ import (
"net/http"
_ "net/http/pprof"
"os"
"path/filepath"
"runtime"
"github.com/XinFinOrg/XDPoSChain/log"
"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"
"golang.org/x/exp/slog"
"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 := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(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.JSONHandler(output)
case logFmtFlag == "json":
handler = log.JSONHandler(output)
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)
}

200
internal/testlog/testlog.go Normal file
View file

@ -0,0 +1,200 @@
// 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"
"sync"
"testing"
"github.com/XinFinOrg/XDPoSChain/log"
"golang.org/x/exp/slog"
)
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) Write(level slog.Level, msg string, ctx ...interface{}) {}
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 {
rawVal := attr.Value.Any()
fmt.Fprintf(buf, " %s=%s", attr.Key, log.FormatLogfmtValue(rawVal, true))
}
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
}

View file

@ -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

View file

@ -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.

View file

@ -1,77 +0,0 @@
![obligatory xkcd](https://imgs.xkcd.com/comics/standards.png)
# log15 [![godoc reference](https://godoc.org/github.com/inconshreveable/log15?status.png)](https://godoc.org/github.com/inconshreveable/log15) [![Build Status](https://travis-ci.org/inconshreveable/log15.svg?branch=master)](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`](https://golang.org/pkg/io/) and [`net/http`](https://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](https://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

View file

@ -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

View file

@ -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 formatted. 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

View file

@ -2,18 +2,15 @@ package log
import (
"bytes"
"encoding/json"
"fmt"
"math/big"
"reflect"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"unicode/utf8"
"github.com/holiman/uint256"
"golang.org/x/exp/slog"
)
const (
@ -24,61 +21,19 @@ const (
termCtxMaxPadding = 40
)
// ResetGlobalState resets the fieldPadding, which is useful for producing
// predictable output.
func ResetGlobalState() {
fieldPaddingLock.Lock()
fieldPadding = make(map[string]int)
fieldPaddingLock.Unlock()
}
// 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) {
locationEnabled.Store(print)
if print {
stackEnabled.Store(true)
}
}
// stackEnabled is an atomic flag controlling whether the log handler needs
// to store the callsite stack. This is needed in case any handler wants to
// print locations (locationEnabled), use vmodule, or print full stacks (BacktraceAt).
var stackEnabled atomic.Bool
// locationEnabled is an atomic flag controlling whether the terminal formatter
// should append the log locations too when printing entries.
var locationEnabled atomic.Bool
// locationLength is the maxmimum path length encountered, which all logs are
// padded to to aid in alignment.
var locationLength atomic.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
Format(r slog.Record) []byte
}
// FormatFunc returns a new Format object which uses
// the given function to perform record formatting.
func FormatFunc(f func(*Record) []byte) Format {
func FormatFunc(f func(slog.Record) []byte) Format {
return formatFunc(f)
}
type formatFunc func(*Record) []byte
type formatFunc func(slog.Record) []byte
func (f formatFunc) Format(r *Record) []byte {
func (f formatFunc) Format(r slog.Record) []byte {
return f(r)
}
@ -89,263 +44,100 @@ 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.
//
// [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 TerminalFormat(usecolor bool) Format {
return FormatFunc(func(r *Record) []byte {
msg := escapeMessage(r.Msg)
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
}
func (h *TerminalHandler) TerminalFormat(r slog.Record, usecolor bool) []byte {
msg := escapeMessage(r.Message)
var color = 0
if usecolor {
switch r.Level {
case LevelCrit:
color = 35
case slog.LevelError:
color = 31
case slog.LevelWarn:
color = 33
case slog.LevelInfo:
color = 32
case slog.LevelDebug:
color = 36
case LevelTrace:
color = 34
}
}
b := &bytes.Buffer{}
lvl := r.Lvl.AlignedString()
if locationEnabled.Load() {
// 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(locationLength.Load())
if align < len(location) {
align = len(location)
locationLength.Store(uint32(align))
}
padding := strings.Repeat(" ", align-len(location))
b := &bytes.Buffer{}
lvl := LevelAlignedString(r.Level)
if color > 0 {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), msg)
} else {
fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), msg)
}
// try to justify the log output for short messages
length := utf8.RuneCountInString(msg)
if r.NumAttrs() > 0 && length < termMsgJust {
b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length))
}
// print the keys logfmt style
h.logfmt(b, r, color)
// 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, msg)
} else {
fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, msg)
}
} else {
if color > 0 {
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), msg)
} else {
fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), msg)
}
}
// try to justify the log output for short messages
length := utf8.RuneCountInString(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()
})
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 (h *TerminalHandler) logfmt(buf *bytes.Buffer, r slog.Record, color int) {
attrs := []slog.Attr{}
r.Attrs(func(attr slog.Attr) bool {
attrs = append(attrs, attr)
return true
})
}
func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) {
for i := 0; i < len(ctx); i += 2 {
attrs = append(h.attrs, attrs...)
for i, attr := range attrs {
if i != 0 {
buf.WriteByte(' ')
}
k, ok := ctx[i].(string)
v := formatLogfmtValue(ctx[i+1], term)
if !ok {
k, v = errorKey, fmt.Sprintf("%+T is not a string key", ctx[i])
} else {
k = escapeString(k)
}
key := escapeString(attr.Key)
rawVal := attr.Value.Any()
val := FormatLogfmtValue(rawVal, true)
// XXX: we should probably check that all of your key bytes aren't invalid
fieldPaddingLock.RLock()
padding := fieldPadding[k]
fieldPaddingLock.RUnlock()
// TODO (jwasinger) above comment was from log15 code. what does it mean? check that key bytes are ascii characters?
padding := h.fieldPadding[key]
length := utf8.RuneCountInString(v)
length := utf8.RuneCountInString(val)
if padding < length && length <= termCtxMaxPadding {
padding = length
fieldPaddingLock.Lock()
fieldPadding[k] = padding
fieldPaddingLock.Unlock()
h.fieldPadding[key] = padding
}
if color > 0 {
fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, k)
fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, key)
} else {
buf.WriteString(k)
buf.WriteString(key)
buf.WriteByte('=')
}
buf.WriteString(v)
if i < len(ctx)-2 && padding > length {
buf.WriteString(val)
if i < r.NumAttrs()-1 && padding > length {
buf.Write(bytes.Repeat([]byte{' '}, padding-length))
}
}
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)
}
// JSONFormatOrderedEx formats log records as JSON arrays. 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 JSONFormatOrderedEx(pretty, lineSeparated bool) Format {
jsonMarshal := json.Marshal
if pretty {
jsonMarshal = func(v interface{}) ([]byte, error) {
return json.MarshalIndent(v, "", " ")
}
// formatValue formats a value for serialization
func FormatLogfmtValue(value interface{}, term bool) (result string) {
if value == nil {
return "<nil>"
}
return FormatFunc(func(r *Record) []byte {
props := map[string]interface{}{
r.KeyNames.Time: r.Time,
r.KeyNames.Lvl: r.Lvl.String(),
r.KeyNames.Msg: r.Msg,
}
ctx := make([]string, len(r.Ctx))
for i := 0; i < len(r.Ctx); i += 2 {
if k, ok := r.Ctx[i].(string); ok {
ctx[i] = k
ctx[i+1] = formatLogfmtValue(r.Ctx[i+1], true)
} else {
props[errorKey] = fmt.Sprintf("%+T is not a string key,", r.Ctx[i])
}
}
props[r.KeyNames.Ctx] = ctx
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
})
}
// 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 := map[string]interface{}{
r.KeyNames.Time: r.Time,
r.KeyNames.Lvl: r.Lvl.String(),
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("%+T is not a string key", r.Ctx[i])
} else {
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{}) {
defer func() {
if err := recover(); err != nil {
if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() {
result = "nil"
result = "<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"
}
switch v := value.(type) {
case time.Time:
// Performance optimization: No need for escaping since the provided
@ -375,8 +167,11 @@ func formatLogfmtValue(value interface{}, term bool) string {
return escapeString(s.TerminalString())
}
}
value = formatShared(value)
switch v := value.(type) {
case error:
return escapeString(v.Error())
case fmt.Stringer:
return escapeString(v.String())
case bool:
return strconv.FormatBool(v)
case float32:

View file

@ -1,301 +1,25 @@
package log
import (
"context"
"fmt"
"io"
"net"
"os"
"math/big"
"reflect"
"sync"
"sync/atomic"
"time"
"github.com/go-stack/stack"
"github.com/holiman/uint256"
"golang.org/x/exp/slog"
)
// Handler defines where and how log records are written.
// A Logger prints its log records by writing to a Handler.
// 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
}
// FuncHandler returns a Handler that logs records with the given
// function.
func FuncHandler(fn func(r *Record) error) Handler {
return funcHandler(fn)
}
type funcHandler func(r *Record) error
func (h funcHandler) Log(r *Record) error {
return h(r)
}
// 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.
// 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.
//
// 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))
}
// 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 {
mu.Lock()
defer mu.Unlock()
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
}
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
}
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
}
func (h *closingHandler) Close() error {
return h.WriteCloser.Close()
}
// 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)
})
}
// 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)
})
}
// 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 formatted 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)
})
}
// 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
})
}
// 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)
}
// 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)
}
// 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
})
}
// 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
}
r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err)
}
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)
}
}()
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
}
}
}
if hadErr {
r.Ctx = append(r.Ctx, errorKey, "bad lazy")
}
return h.Log(r)
})
// 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{}
}
func evaluateLazy(lz Lazy) (interface{}, error) {
@ -325,51 +49,175 @@ func evaluateLazy(lz Lazy) (interface{}, error) {
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
type discardHandler struct{}
// DiscardHandler returns a no-op handler
func DiscardHandler() slog.Handler {
return &discardHandler{}
}
func (h *discardHandler) Handle(_ context.Context, r slog.Record) error {
return nil
}
func (h *discardHandler) Enabled(_ context.Context, level slog.Level) bool {
return false
}
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
}
// 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.
//
// [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)
}
// 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),
}
}
func (h *TerminalHandler) Handle(_ context.Context, r slog.Record) error {
h.mu.Lock()
defer h.mu.Unlock()
h.wr.Write(h.TerminalFormat(r, h.useColor))
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),
}
}
// 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()
}
type leveler struct{ minLevel slog.Level }
func (l *leveler) Level() slog.Level {
return l.minLevel
}
func JSONHandler(wr io.Writer) slog.Handler {
return slog.NewJSONHandler(wr, &slog.HandlerOptions{
ReplaceAttr: builtinReplaceJSON,
})
}
// Must 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
// 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,
})
}
func must(h Handler, err error) Handler {
if err != nil {
panic(err)
// 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},
})
}
func builtinReplaceLogfmt(_ []string, attr slog.Attr) slog.Attr {
return builtinReplace(nil, attr, true)
}
func builtinReplaceJSON(_ []string, attr slog.Attr) slog.Attr {
return builtinReplace(nil, attr, false)
}
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 {
return slog.Attr{Key: "t", Value: attr.Value}
}
}
case slog.LevelKey:
if l, ok := attr.Value.Any().(slog.Level); ok {
attr = slog.Any("lvl", LevelString(l))
return attr
}
}
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))
}
// 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)
switch v := attr.Value.Any().(type) {
case time.Time:
if logfmt {
attr = slog.String(attr.Key, v.Format(timeFormat))
}
case *big.Int:
if v == nil {
attr.Value = slog.StringValue("<nil>")
} else {
attr.Value = slog.StringValue(v.String())
}
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 attr
}

View file

@ -17,6 +17,7 @@
package log
import (
"context"
"errors"
"fmt"
"regexp"
@ -25,54 +26,47 @@ import (
"strings"
"sync"
"sync/atomic"
"golang.org/x/exp/slog"
)
// 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 atomic.Uint32 // Current log level, atomically accessible
override atomic.Bool // Flag whether overrides are used, atomically accessible
backtrace atomic.Bool // 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,
}
}
// SetHandler updates the handler to write records to the specified sub-handler.
func (h *GlogHandler) SetHandler(nh Handler) {
h.origin = nh
}
// pattern contains a filter for the Vmodule option, holding a verbosity level
// 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) {
h.level.Store(uint32(level))
func (h *GlogHandler) Verbosity(level slog.Level) {
h.level.Store(int32(level))
}
// Vmodule sets the glog verbosity pattern.
@ -108,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
@ -130,108 +126,84 @@ 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)
h.siteCache = make(map[uintptr]slog.Level)
h.override.Store(len(filter) != 0)
// Enable location storage (globally)
if len(h.patterns) > 0 {
stackEnabled.Store(true)
}
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.
//
// 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()
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
}
h.location = location
h.backtrace.Store(len(location) > 0)
// Enable location storage (globally)
stackEnabled.Store(true)
return nil
func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
h.lock.RLock()
siteCache := make(map[uintptr]slog.Level)
for k, v := range h.siteCache {
siteCache[k] = v
}
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
}
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 h.backtrace.Load() {
// 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)
}
}
func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error {
// If the global log level allows, fast track logging
if h.level.Load() >= uint32(r.Lvl) {
return h.origin.Log(r)
}
// If no local overrides are present, fast track skipping
if !h.override.Load() {
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.Frame().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.Frame().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.Frame().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
}

View file

@ -1,293 +1,222 @@
package log
import (
"fmt"
"context"
"math"
"os"
"runtime"
"time"
"github.com/go-stack/stack"
"golang.org/x/exp/slog"
)
const timeKey = "t"
const lvlKey = "lvl"
const msgKey = "msg"
const ctxKey = "ctx"
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
)
// AlignedString 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
)
// convert 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"
}
}
// String returns the name of a Lvl.
func (l Lvl) String() string {
// LevelString returns a 5-character 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:
case slog.LevelError:
return "eror"
case LvlCrit:
case LevelCrit:
return "crit"
default:
panic("bad level")
return "unknown"
}
}
// LvlFromString 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
}
// RecordKeyNames gets stored in a Record when the write function is executed.
type RecordKeyNames struct {
Time string
Msg string
Lvl string
Ctx 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
// With 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 trace level with context key/value pairs
//
// # Usage
//
// log.Trace("msg")
// log.Trace("msg", "key1", val1)
// log.Trace("msg", "key1", val1, "key2", val2)
// Trace log a message at the trace level with context key/value pairs
Trace(msg string, ctx ...interface{})
// 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)
// Debug logs a message at the debug level with context key/value pairs
Debug(msg string, ctx ...interface{})
// 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)
// Info logs a message at the info level with context key/value pairs
Info(msg string, ctx ...interface{})
// 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)
// Warn logs a message at the warn level with context key/value pairs
Warn(msg string, ctx ...interface{})
// 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)
// Error logs a message at the error level with context key/value pairs
Error(msg string, ctx ...interface{})
// 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)
// 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)
}
type logger struct {
ctx []interface{}
h *swapHandler
inner *slog.Logger
}
func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) {
record := &Record{
Time: time.Now(),
Lvl: lvl,
Msg: msg,
Ctx: newContext(l.ctx, ctx),
KeyNames: RecordKeyNames{
Time: timeKey,
Msg: msgKey,
Lvl: lvlKey,
Ctx: ctxKey,
},
// NewLogger returns a logger with the specified handler set
func NewLogger(h slog.Handler) Logger {
return &logger{
slog.New(h),
}
if stackEnabled.Load() {
record.Call = stack.Caller(2)
}
// 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
}
l.h.Log(record)
}
func (l *logger) New(ctx ...interface{}) Logger {
child := &logger{newContext(l.ctx, ctx), new(swapHandler)}
child.SetHandler(l.h)
return child
}
var pcs [1]uintptr
runtime.Callers(3, pcs[:])
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
}
if len(attrs)%2 != 0 {
attrs = append(attrs, nil, errorKey, "Normalized odd number of arguments by adding nil")
}
func (l *logger) Trace(msg string, ctx ...interface{}) {
l.write(msg, LvlTrace, ctx)
}
func (l *logger) Debug(msg string, ctx ...interface{}) {
l.write(msg, LvlDebug, ctx)
}
func (l *logger) Info(msg string, ctx ...interface{}) {
l.write(msg, LvlInfo, ctx)
}
func (l *logger) Warn(msg string, ctx ...interface{}) {
l.write(msg, LvlWarn, ctx)
}
func (l *logger) Error(msg string, ctx ...interface{}) {
l.write(msg, LvlError, ctx)
}
func (l *logger) Crit(msg string, ctx ...interface{}) {
l.write(msg, LvlCrit, 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()
// evaluate lazy values
var hadErr bool
for i := 1; i < len(attrs); i += 2 {
lz, ok := attrs[i].(Lazy)
if ok {
v, err := evaluateLazy(lz)
if err != nil {
hadErr = true
attrs[i] = err
} else {
attrs[i] = v
}
}
}
// 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")
if hadErr {
attrs = append(attrs, errorKey, "bad lazy")
}
return ctx
r := slog.NewRecord(time.Now(), level, msg, pcs[0])
r.Add(attrs...)
l.inner.Handler().Handle(context.Background(), r)
}
// 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{}
func (l *logger) Log(level slog.Level, msg string, attrs ...any) {
l.Write(level, msg, attrs...)
}
// 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
func (l *logger) With(ctx ...interface{}) Logger {
return &logger{l.inner.With(ctx...)}
}
func (l *logger) New(ctx ...interface{}) Logger {
return l.With(ctx...)
}
func (l *logger) Trace(msg string, ctx ...interface{}) {
l.Write(LevelTrace, msg, ctx...)
}
func (l *logger) Debug(msg string, ctx ...interface{}) {
l.Write(slog.LevelDebug, msg, ctx...)
}
func (l *logger) Info(msg string, ctx ...interface{}) {
l.Write(slog.LevelInfo, msg, 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(slog.LevelError, msg, ctx...)
}
func (l *logger) Crit(msg string, ctx ...interface{}) {
l.Write(LevelCrit, msg, ctx...)
os.Exit(1)
}

View file

@ -5,48 +5,18 @@ import (
"os"
"strings"
"testing"
)
// TestLoggingWithTrace checks that if BackTraceAt is set, then the
// gloghandler is capable of spitting out a stacktrace
func TestLoggingWithTrace(t *testing.T) {
defer stackEnabled.Store(stackEnabled.Load())
out := new(bytes.Buffer)
logger := New()
{
glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false)))
glog.Verbosity(LvlTrace)
if err := glog.BacktraceAt("logger_test.go:24"); err != nil {
t.Fatal(err)
}
logger.SetHandler(glog)
}
logger.Trace("a message", "foo", "bar") // Will be bumped to INFO
have := out.String()
if !strings.HasPrefix(have, "INFO") {
t.Fatalf("backtraceat should bump level to info: %s", have)
}
// The timestamp is locale-dependent, so we want to trim that off
// "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..."
have = strings.Split(have, "]")[1]
wantPrefix := " a message\n\ngoroutine"
if !strings.HasPrefix(have, wantPrefix) {
t.Errorf("\nhave: %q\nwant: %q\n", have, wantPrefix)
}
}
"golang.org/x/exp/slog"
)
// TestLoggingWithVmodule checks that vmodule works.
func TestLoggingWithVmodule(t *testing.T) {
defer stackEnabled.Store(stackEnabled.Load())
out := new(bytes.Buffer)
logger := New()
{
glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false)))
glog.Verbosity(LvlCrit)
logger.SetHandler(glog)
logger.Warn("This should not be seen", "ignored", "true")
glog.Vmodule("logger_test.go=5")
}
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
@ -58,8 +28,24 @@ func TestLoggingWithVmodule(t *testing.T) {
}
}
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 messag ..." -> "a messag..."
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)
}
}
func BenchmarkTraceLogging(b *testing.B) {
Root().SetHandler(LvlFilterHandler(LvlInfo, StreamHandler(os.Stderr, TerminalFormat(true))))
SetDefault(NewLogger(NewTerminalHandler(os.Stderr, true)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
Trace("a message", "v", i)

View file

@ -2,31 +2,33 @@ package log
import (
"os"
"sync/atomic"
"golang.org/x/exp/slog"
)
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())
defaultLogger := &logger{slog.New(DiscardHandler())}
SetDefault(defaultLogger)
}
// 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...)
// 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
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
// 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
@ -39,7 +41,7 @@ func Root() Logger {
// log.Trace("msg", "key1", val1)
// log.Trace("msg", "key1", val1, "key2", val2)
func Trace(msg string, ctx ...interface{}) {
root.write(msg, LvlTrace, ctx)
Root().Write(LevelTrace, msg, ctx...)
}
// Debug is a convenient alias for Root().Debug
@ -52,7 +54,7 @@ func Trace(msg string, ctx ...interface{}) {
// log.Debug("msg", "key1", val1)
// log.Debug("msg", "key1", val1, "key2", val2)
func Debug(msg string, ctx ...interface{}) {
root.write(msg, LvlDebug, ctx)
Root().Write(slog.LevelDebug, msg, ctx...)
}
// Info is a convenient alias for Root().Info
@ -65,7 +67,7 @@ func Debug(msg string, ctx ...interface{}) {
// log.Info("msg", "key1", val1)
// log.Info("msg", "key1", val1, "key2", val2)
func Info(msg string, ctx ...interface{}) {
root.write(msg, LvlInfo, ctx)
Root().Write(slog.LevelInfo, msg, ctx...)
}
// Warn is a convenient alias for Root().Warn
@ -78,7 +80,7 @@ func Info(msg string, ctx ...interface{}) {
// log.Warn("msg", "key1", val1)
// log.Warn("msg", "key1", val1, "key2", val2)
func Warn(msg string, ctx ...interface{}) {
root.write(msg, LvlWarn, ctx)
Root().Write(slog.LevelWarn, msg, ctx...)
}
// Error is a convenient alias for Root().Error
@ -91,7 +93,7 @@ func Warn(msg string, ctx ...interface{}) {
// log.Error("msg", "key1", val1)
// log.Error("msg", "key1", val1, "key2", val2)
func Error(msg string, ctx ...interface{}) {
root.write(msg, LvlError, ctx)
Root().Write(slog.LevelError, msg, ctx...)
}
// Crit is a convenient alias for Root().Crit
@ -104,6 +106,12 @@ func Error(msg string, ctx ...interface{}) {
// log.Crit("msg", "key1", val1)
// log.Crit("msg", "key1", val1, "key2", val2)
func Crit(msg string, ctx ...interface{}) {
root.write(msg, LvlCrit, ctx)
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().With(ctx...)
}

View file

@ -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))
}

View file

@ -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())

View file

@ -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)))
}

View file

@ -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 {

View file

@ -42,6 +42,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/rpc"
"github.com/docker/docker/pkg/reexec"
"github.com/gorilla/websocket"
"golang.org/x/exp/slog"
)
// ExecAdapter is a NodeAdapter which runs simulation nodes by executing the
@ -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
// 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
func execP2PNode() {
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.LogfmtFormat()))
glogger.Verbosity(log.LvlInfo)
log.Root().SetHandler(glogger)
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 an environment variable.
func execP2PNode() {
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

View file

@ -30,6 +30,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/rpc"
"github.com/docker/docker/pkg/reexec"
"github.com/gorilla/websocket"
"golang.org/x/exp/slog"
)
// Node represents a node in a simulation network which is created by a
@ -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
}

View file

@ -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{