Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce SetCustomReceiver in the logger #4507

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 61 additions & 0 deletions ecs-agent/logger/custom_receiver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package logger

import (
"fmt"

"github.com/cihub/seelog"
)

// CustomReceiver defines the interface for custom logging implementations
type CustomReceiver interface {
GetOutputFormat() string
Trace(message string)
Debug(message string)
Info(message string)
Warn(message string)
Error(message string)
Critical(message string)
Flush() error
Close() error
}

// internal wrapper that implements seelog.CustomReceiver
type customReceiverWrapper struct {
receiver CustomReceiver
}

func (w *customReceiverWrapper) ReceiveMessage(message string, level seelog.LogLevel, context seelog.LogContextInterface) error {

switch level {
case seelog.TraceLvl:
w.receiver.Trace(message)
case seelog.DebugLvl:
w.receiver.Debug(message)
case seelog.InfoLvl:
w.receiver.Info(message)
case seelog.WarnLvl:
w.receiver.Warn(message)
case seelog.ErrorLvl:
w.receiver.Error(message)
case seelog.CriticalLvl:
w.receiver.Critical(message)
default:
fmt.Printf("Unhandled level: %v", level)
}
return nil
}

func (w *customReceiverWrapper) AfterParse(initArgs seelog.CustomReceiverInitArgs) error {
return nil
}

func (w *customReceiverWrapper) Flush() {
err := w.receiver.Flush()
if err != nil {
fmt.Printf("Couldn't flush the logger due to: %v", err)
}
}

func (w *customReceiverWrapper) Close() error {
return w.receiver.Close()
}
55 changes: 51 additions & 4 deletions ecs-agent/logger/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,49 @@ func SetLogToStdout(duplicate bool) {
reloadConfig()
}

// SetCustomReceiver configures the ECS Agent logger to use a custom logger implementation.
// This allows external applications to intercept and handle ECS Agent logs in their own way,
// such as sending logs to a custom destination or formatting them differently.
// More details can be found here: https://github.com/cihub/seelog/wiki/Custom-receivers
// The custom receiver must implement the CustomReceiver interface, which requires:
// - GetTimestampFormat(): returns the desired timestamp format (e.g., "2006-01-02T15:04:05Z07:00")
// - GetOutputFormat(): returns the desired output format ("logfmt", "json", or "windows")
// - Log handling methods (Debug, Info, Warn, etc.)
func SetCustomReceiver(receiver CustomReceiver) {
registerCustomFormatters()
wrapper := &customReceiverWrapper{receiver: receiver}
outputFormat := receiver.GetOutputFormat()

// Internal seelog configuration
customConfig := `
<seelog type="asyncloop">
<outputs>
<custom name="customReceiver" formatid="` + outputFormat + `"/>
</outputs>
<formats>
<format id="` + logFmt + `" format="%EcsAgentLogfmt" />
<format id="` + jsonFmt + `" format="%EcsAgentJson" />
<format id="windows" format="%EcsMsg" />
</formats>
</seelog>
`

parserParams := &seelog.CfgParseParams{
CustomReceiverProducers: map[string]seelog.CustomReceiverProducer{
"customReceiver": func(seelog.CustomReceiverInitArgs) (seelog.CustomReceiver, error) {
return wrapper, nil
},
},
}

replacementLogger, err := seelog.LoggerFromParamConfigAsString(customConfig, parserParams)
if err != nil {
fmt.Println("Failed to create a replacement logger", err)
}

setGlobalLogger(replacementLogger, outputFormat)
}

func init() {
Config = &logConfig{
logfile: os.Getenv(LOGFILE_ENV_VAR),
Expand All @@ -352,10 +395,7 @@ func init() {
}
}

// InitSeelog registers custom logging formats, updates the internal Config struct
// and reloads the global logger. This should only be called once, as external
// callers should use the Config struct over environment variables directly.
func InitSeelog() {
func registerCustomFormatters() {
if err := seelog.RegisterCustomFormatter("EcsAgentLogfmt", logfmtFormatter); err != nil {
seelog.Error(err)
}
Expand All @@ -365,6 +405,13 @@ func InitSeelog() {
if err := seelog.RegisterCustomFormatter("EcsMsg", ecsMsgFormatter); err != nil {
seelog.Error(err)
}
}

// InitSeelog registers custom logging formats, updates the internal Config struct
// and reloads the global logger. This should only be called once, as external
// callers should use the Config struct over environment variables directly.
func InitSeelog() {
registerCustomFormatters()

if DriverLogLevel := os.Getenv(LOGLEVEL_ENV_VAR); DriverLogLevel != "" {
SetDriverLogLevel(DriverLogLevel)
Expand Down
Loading
Loading