Skip to content

Commit

Permalink
feat: introduce customreceiver in logging
Browse files Browse the repository at this point in the history
  • Loading branch information
niyatim23 committed Mar 7, 2025
1 parent ade2808 commit cc2fd2e
Show file tree
Hide file tree
Showing 7 changed files with 451 additions and 17 deletions.

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

0 comments on commit cc2fd2e

Please sign in to comment.