Logrus hooks for terminal vs log-file output (#16102)

## Review after #16059 

**What type of PR is this?**
Feature

**What does this PR do?**
This PR introduces logrus writer hooks into the logging of prysm.
when log-format is text:
- set the default logrus output to be `io.Discard`
- create a writer hook for terminal, with formatting and coloring
enabled.
- create a separate writer hook for log-file (if enabled), without
coloring.

This immediately allows for having formatted/colored terminal logs,
while keeping the log-file clean.
This commit is contained in:
Bastin
2026-01-05 16:20:12 +01:00
committed by GitHub
parent 6b5ba5ad01
commit 6fa0e9cf5f
9 changed files with 86 additions and 17 deletions

View File

@@ -0,0 +1,4 @@
### Added
- Added separate logrus hooks for handling the formatting and output of terminal logs vs log-file logs, instead of the
default logrus output.

View File

@@ -4,6 +4,7 @@ package main
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
runtimeDebug "runtime/debug"
@@ -172,14 +173,20 @@ func before(ctx *cli.Context) error {
switch format {
case "text":
// disabling logrus default output so we can control it via different hooks
logrus.SetOutput(io.Discard)
// create a custom formatter and hook for terminal output
formatter := new(prefixed.TextFormatter)
formatter.TimestampFormat = "2006-01-02 15:04:05.00"
formatter.FullTimestamp = true
formatter.ForceFormatting = true
formatter.ForceColors = true
// 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) != ""
logrus.SetFormatter(formatter)
logrus.AddHook(&logs.WriterHook{
Formatter: formatter,
Writer: os.Stderr,
})
case "fluentd":
f := joonix.NewFormatter()
@@ -202,7 +209,7 @@ func before(ctx *cli.Context) error {
logFileName := ctx.String(cmd.LogFileName.Name)
if logFileName != "" {
if err := logs.ConfigurePersistentLogging(logFileName); err != nil {
if err := logs.ConfigurePersistentLogging(logFileName, format); err != nil {
log.WithError(err).Error("Failed to configuring logging to disk.")
}
}

View File

@@ -86,7 +86,7 @@ func main() {
logFileName := ctx.String(cmd.LogFileName.Name)
if logFileName != "" {
if err := logs.ConfigurePersistentLogging(logFileName); err != nil {
if err := logs.ConfigurePersistentLogging(logFileName, format); err != nil {
log.WithError(err).Error("Failed to configuring logging to disk.")
}
}

View File

@@ -5,6 +5,7 @@ package main
import (
"fmt"
"io"
"os"
"path/filepath"
runtimeDebug "runtime/debug"
@@ -151,13 +152,20 @@ func main() {
format := ctx.String(cmd.LogFormat.Name)
switch format {
case "text":
// disabling logrus default output so we can control it via different hooks
logrus.SetOutput(io.Discard)
// create a custom formatter and hook for terminal output
formatter := new(prefixed.TextFormatter)
formatter.TimestampFormat = "2006-01-02 15:04:05.00"
formatter.FullTimestamp = true
// 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 = logFileName != ""
logrus.SetFormatter(formatter)
formatter.ForceFormatting = true
formatter.ForceColors = true
logrus.AddHook(&logs.WriterHook{
Formatter: formatter,
Writer: os.Stderr,
})
case "fluentd":
f := joonix.NewFormatter()
if err := joonix.DisableTimestampFormat(f); err != nil {
@@ -177,7 +185,7 @@ func main() {
}
if logFileName != "" {
if err := logs.ConfigurePersistentLogging(logFileName); err != nil {
if err := logs.ConfigurePersistentLogging(logFileName, format); err != nil {
log.WithError(err).Error("Failed to configuring logging to disk.")
}
}

View File

@@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"hook.go",
"logutil.go",
"stream.go",
],
@@ -14,6 +15,7 @@ go_library(
"//config/params:go_default_library",
"//crypto/rand:go_default_library",
"//io/file:go_default_library",
"//runtime/logging/logrus-prefixed-formatter:go_default_library",
"@com_github_hashicorp_golang_lru//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],

29
io/logs/hook.go Normal file
View File

@@ -0,0 +1,29 @@
package logs
import (
"io"
"github.com/sirupsen/logrus"
)
type WriterHook struct {
AllowedLevels []logrus.Level
Writer io.Writer
Formatter logrus.Formatter
}
func (hook *WriterHook) Levels() []logrus.Level {
if hook.AllowedLevels == nil || len(hook.AllowedLevels) == 0 {
return logrus.AllLevels
}
return hook.AllowedLevels
}
func (hook *WriterHook) Fire(entry *logrus.Entry) error {
line, err := hook.Formatter.Format(entry)
if err != nil {
return err
}
_, err = hook.Writer.Write(line)
return err
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/io/file"
prefixed "github.com/OffchainLabs/prysm/v7/runtime/logging/logrus-prefixed-formatter"
"github.com/sirupsen/logrus"
)
@@ -20,7 +21,7 @@ func addLogWriter(w io.Writer) {
}
// ConfigurePersistentLogging adds a log-to-file writer. File content is identical to stdout.
func ConfigurePersistentLogging(logFileName string) error {
func ConfigurePersistentLogging(logFileName string, format string) error {
logrus.WithField("logFileName", logFileName).Info("Logs will be made persistent")
if err := file.MkdirAll(filepath.Dir(logFileName)); err != nil {
return err
@@ -30,7 +31,25 @@ func ConfigurePersistentLogging(logFileName string) error {
return err
}
addLogWriter(f)
if format != "text" {
addLogWriter(f)
logrus.Info("File logging initialized")
return nil
}
// Create formatter and writer hook
formatter := new(prefixed.TextFormatter)
formatter.TimestampFormat = "2006-01-02 15:04:05.00"
formatter.FullTimestamp = true
// 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 = true
logrus.AddHook(&WriterHook{
Formatter: formatter,
Writer: f,
})
logrus.Info("File logging initialized")
return nil

View File

@@ -34,13 +34,13 @@ func TestConfigurePersistantLogging(t *testing.T) {
logFileName := "test.log"
existingDirectory := "test-1-existing-testing-dir"
err := ConfigurePersistentLogging(fmt.Sprintf("%s/%s/%s", testParentDir, existingDirectory, logFileName))
err := ConfigurePersistentLogging(fmt.Sprintf("%s/%s/%s", testParentDir, existingDirectory, logFileName), "text")
require.NoError(t, err)
// 2. Test creation of file along with parent directory
nonExistingDirectory := "test-2-non-existing-testing-dir"
err = ConfigurePersistentLogging(fmt.Sprintf("%s/%s/%s", testParentDir, nonExistingDirectory, logFileName))
err = ConfigurePersistentLogging(fmt.Sprintf("%s/%s/%s", testParentDir, nonExistingDirectory, logFileName), "text")
require.NoError(t, err)
// 3. Test creation of file in an existing parent directory with a non-existing sub-directory
@@ -51,7 +51,7 @@ func TestConfigurePersistantLogging(t *testing.T) {
return
}
err = ConfigurePersistentLogging(fmt.Sprintf("%s/%s/%s/%s", testParentDir, existingDirectory, nonExistingSubDirectory, logFileName))
err = ConfigurePersistentLogging(fmt.Sprintf("%s/%s/%s/%s", testParentDir, existingDirectory, nonExistingSubDirectory, logFileName), "text")
require.NoError(t, err)
//4. Create log file in a directory without 700 permissions

View File

@@ -71,7 +71,7 @@ func main() {
flag.Parse()
if *logFileName != "" {
if err := logs.ConfigurePersistentLogging(*logFileName); err != nil {
if err := logs.ConfigurePersistentLogging(*logFileName, "text"); err != nil {
log.WithError(err).Error("Failed to configuring logging to disk.")
}
}