Compare commits

...

1 Commits

Author SHA1 Message Date
Bastin
850b7326eb per package visibility 2026-01-08 16:38:50 +01:00
8 changed files with 131 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
### Added
- Added `--log-only` and `--log-exclude` flags to the beacon chain client to allow users to filter log output.

View File

@@ -113,6 +113,8 @@ var appFlags = []cli.Flag{
cmd.PubsubQueueSize,
cmd.DataDirFlag,
cmd.VerbosityFlag,
cmd.LogOnlyFlag,
cmd.LogExcludeFlag,
cmd.EnableTracingFlag,
cmd.TracingProcessNameFlag,
cmd.TracingEndpointFlag,
@@ -191,6 +193,10 @@ func before(ctx *cli.Context) error {
formatter.FullTimestamp = true
formatter.ForceFormatting = true
formatter.ForceColors = true
logOnly, logExclude := cmd.ParseLogPackageFlags(ctx)
formatter.LogOnly = logOnly
formatter.LogExclude = logExclude
formatter.RespectLogFilters = true
logrus.AddHook(&logs.WriterHook{
Formatter: formatter,

View File

@@ -201,6 +201,8 @@ var appHelpFlagGroups = []flagGroup{
cmd.LogFileName,
cmd.VerbosityFlag,
flags.DisableEphemeralLogFile,
cmd.LogExcludeFlag,
cmd.LogOnlyFlag,
},
},
{ // Feature flags.

View File

@@ -62,6 +62,9 @@ func main() {
formatter := new(prefixed.TextFormatter)
formatter.TimestampFormat = "2006-01-02 15:04:05.00"
formatter.FullTimestamp = true
logOnly, logExclude := cmd.ParseLogPackageFlags(ctx)
formatter.LogOnly = logOnly
formatter.LogExclude = logExclude
// If persistent log files are written - we disable the log messages coloring because
// the colors are ANSI codes and seen as gibberish in the log files.
formatter.DisableColors = ctx.String(cmd.LogFileName.Name) != ""

View File

@@ -36,6 +36,16 @@ var (
Usage: "Logging verbosity. (trace, debug, info, warn, error, fatal, panic)",
Value: "info",
}
// LogOnlyFlag specifies the packages for which logging is enabled.
LogOnlyFlag = &cli.StringSliceFlag{
Name: "log-only",
Usage: "Enables logging only for the specified packages (comma-separated package paths). By default, all packages are logged.",
}
// LogExcludeFlag specifies the packages for which logging is disabled.
LogExcludeFlag = &cli.StringSliceFlag{
Name: "log-exclude",
Usage: "Disables logging for the specified packages (comma-separated package paths).",
}
// DataDirFlag defines a path on disk where Prysm databases are stored.
DataDirFlag = &cli.StringFlag{
Name: "datadir",
@@ -283,6 +293,25 @@ var (
}
)
// ParseLogPackageFlags returns flattened lists for log-only and log-exclude flags.
func ParseLogPackageFlags(ctx *cli.Context) (logOnly []string, logExclude []string) {
return parseCommaSeparatedEntries(ctx.StringSlice(LogOnlyFlag.Name)), parseCommaSeparatedEntries(ctx.StringSlice(LogExcludeFlag.Name))
}
func parseCommaSeparatedEntries(values []string) []string {
entries := make([]string, 0)
for _, value := range values {
for entry := range strings.SplitSeq(value, ",") {
entry = strings.TrimSpace(entry)
entry = strings.TrimSuffix(entry, "/")
if entry != "" {
entries = append(entries, entry)
}
}
}
return entries
}
// LoadFlagsFromConfig sets flags values from config file if ConfigFileFlag is set.
func LoadFlagsFromConfig(cliCtx *cli.Context, flags []cli.Flag) error {
if cliCtx.IsSet(ConfigFileFlag.Name) {

View File

@@ -41,6 +41,21 @@ func TestLoadFlagsFromConfig(t *testing.T) {
require.NoError(t, os.Remove("flags_test.yaml"))
}
func TestParseLogPackageFlags(t *testing.T) {
set := flag.NewFlagSet("test", 0)
require.NoError(t, LogOnlyFlag.Apply(set))
require.NoError(t, LogExcludeFlag.Apply(set))
require.NoError(t, set.Set(LogOnlyFlag.Name, "pkg/a/,pkg/b"))
require.NoError(t, set.Set(LogOnlyFlag.Name, "pkg/c"))
require.NoError(t, set.Set(LogExcludeFlag.Name, " pkg/d ,pkg/e/ "))
ctx := cli.NewContext(&cli.App{}, set, nil)
logOnly, logExclude := ParseLogPackageFlags(ctx)
require.DeepEqual(t, []string{"pkg/a", "pkg/b", "pkg/c"}, logOnly)
require.DeepEqual(t, []string{"pkg/d", "pkg/e"}, logExclude)
}
func TestValidateNoArgs(t *testing.T) {
app := &cli.App{
Before: ValidateNoArgs,

View File

@@ -170,6 +170,9 @@ func main() {
formatter.FullTimestamp = true
formatter.ForceFormatting = true
formatter.ForceColors = true
logOnly, logExclude := cmd.ParseLogPackageFlags(ctx)
formatter.LogOnly = logOnly
formatter.LogExclude = logExclude
logrus.AddHook(&logs.WriterHook{
Formatter: formatter,

View File

@@ -104,6 +104,9 @@ type TextFormatter struct {
// Wrap empty fields in quotes if true.
QuoteEmptyFields bool
// Whether to respect the per-Package log filters.
RespectLogFilters bool
sync.Once
// Pad msg field with spaces on the right for display.
@@ -120,6 +123,10 @@ type TextFormatter struct {
// Timestamp format to use for display when a full timestamp is printed.
TimestampFormat string
// Per-Package log filters parsed from CLI flags.
LogOnly []string
LogExclude []string
}
func getCompiledColor(main string, fallback string) func(string) string {
@@ -168,6 +175,13 @@ func (f *TextFormatter) SetColorScheme(colorScheme *ColorScheme) {
}
func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) {
// check if entry should be dropped based on package filters
if f.RespectLogFilters {
if shouldDrop := f.shouldDropEntry(entry); shouldDrop {
return nil, nil
}
}
var b *bytes.Buffer
keys := make([]string, 0, len(entry.Data))
for k := range entry.Data {
@@ -403,6 +417,62 @@ func (f *TextFormatter) appendValue(b *bytes.Buffer, value any) (err error) {
return
}
func (f *TextFormatter) shouldDropEntry(entry *logrus.Entry) bool {
if len(f.LogOnly) == 0 && len(f.LogExclude) == 0 {
return false
}
// try to get package field
packageField, ok := entry.Data["package"]
if !ok {
return false
}
packageName, ok := packageField.(string)
if !ok {
return false
}
// get best matches. longer match means a more specific match
lengthOfBestIncludeMatch := bestMatchLen(packageName, f.LogOnly)
lengthOfBestExcludeMatch := bestMatchLen(packageName, f.LogExclude)
if len(f.LogOnly) > 0 && lengthOfBestIncludeMatch == 0 {
return true
}
if lengthOfBestExcludeMatch > 0 {
// if we're only excluding, then drop the entry
if len(f.LogOnly) == 0 {
return true
}
// if both include and exclude have matches, compare lengths. prefers include.
if lengthOfBestIncludeMatch >= lengthOfBestExcludeMatch {
return false
}
return true
}
return false
}
// bestMatchLen returns the length (specificity) of the most specific path that matches package name.
// 0 means "no match".
func bestMatchLen(pkg string, paths []string) int {
best := 0
for _, r := range paths {
if r == "" {
continue
}
if r == pkg || strings.HasPrefix(pkg, r+"/") {
if len(r) > best {
best = len(r)
}
}
}
return best
}
// This is to not silently overwrite `time`, `msg` and `level` fields when
// dumping it. If this code wasn't there doing:
//