diff --git a/agent/app/run.go b/agent/app/run.go index e6fdb37ca32..0ee12573c40 100644 --- a/agent/app/run.go +++ b/agent/app/run.go @@ -60,9 +60,11 @@ func Run(arguments []string) int { } if *parsedArgs.LogLevel != "" { - logger.SetLevel(*parsedArgs.LogLevel, *parsedArgs.LogLevel) + logger.SetDriverLogLevel(*parsedArgs.LogLevel) + logger.SetInstanceLogLevel(*parsedArgs.LogLevel) } else { - logger.SetLevel(*parsedArgs.DriverLogLevel, *parsedArgs.InstanceLogLevel) + logger.SetDriverLogLevel(*parsedArgs.DriverLogLevel) + logger.SetInstanceLogLevel(*parsedArgs.InstanceLogLevel) } // Create an Agent object diff --git a/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/logger/log.go b/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/logger/log.go index fc7025f823c..9af0b28168f 100644 --- a/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/logger/log.go +++ b/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/logger/log.go @@ -46,6 +46,16 @@ const ( DEFAULT_MAX_ROLL_COUNT int = 24 ) +// logLevels is the mapping from ECS_LOGLEVEL to Seelog provided levels. +var logLevels = map[string]string{ + "debug": "debug", + "info": "info", + "warn": "warn", + "error": "error", + "crit": "critical", + "none": "off", +} + type logConfig struct { RolloverType string MaxRollCount int @@ -191,33 +201,6 @@ func getLevelList(fileLevel string) string { return levelLists[fileLevel] } -// SetLevel sets the log levels for logging -func SetLevel(driverLogLevel, instanceLogLevel string) { - levels := map[string]string{ - "debug": "debug", - "info": "info", - "warn": "warn", - "error": "error", - "crit": "critical", - "none": "off", - } - - parsedDriverLevel, driverOk := levels[strings.ToLower(driverLogLevel)] - parsedInstanceLevel, instanceOk := levels[strings.ToLower(instanceLogLevel)] - - if instanceOk || driverOk { - Config.lock.Lock() - defer Config.lock.Unlock() - if instanceOk { - Config.instanceLevel = parsedInstanceLevel - } - if driverOk { - Config.driverLevel = parsedDriverLevel - } - reloadConfig() - } -} - // GetLevel gets the log level func GetLevel() string { Config.lock.Lock() @@ -236,12 +219,41 @@ func setInstanceLevelDefault() string { return DEFAULT_LOGLEVEL } +// SetInstanceLogLevel explicitly sets the log level for instance logs. +func SetInstanceLogLevel(instanceLogLevel string) { + parsedLevel, ok := logLevels[strings.ToLower(instanceLogLevel)] + if ok { + Config.lock.Lock() + defer Config.lock.Unlock() + + os.Setenv(LOGLEVEL_ON_INSTANCE_ENV_VAR, parsedLevel) + Config.instanceLevel = parsedLevel + reloadConfig() + } +} + +// SetDriverLogLevel explicitly sets the log level for a custom driver. +func SetDriverLogLevel(driverLogLevel string) { + parsedLevel, ok := logLevels[strings.ToLower(driverLogLevel)] + if ok { + Config.lock.Lock() + defer Config.lock.Unlock() + + os.Setenv(LOGLEVEL_ENV_VAR, parsedLevel) + Config.driverLevel = parsedLevel + reloadConfig() + } +} + // SetConfigLogFile sets the default output file of the logger. func SetConfigLogFile(logFile string) { - Config.lock.Lock() - defer Config.lock.Unlock() + if logFile != "" { + Config.lock.Lock() + defer Config.lock.Unlock() - Config.logfile = logFile + Config.logfile = logFile + reloadConfig() + } } // SetConfigLogFormat sets the output format of the logger. @@ -250,20 +262,20 @@ func SetConfigLogFormat(logFormat string) { Config.lock.Lock() defer Config.lock.Unlock() - os.Setenv(LOG_OUTPUT_FORMAT_ENV_VAR, logFormat) Config.outputFormat = logFormat + reloadConfig() } // SetConfigMaxFileSizeMB sets the max file size of a log file // in Megabytes before the logger rotates to a new file. func SetConfigMaxFileSizeMB(maxSizeInMB float64) { - Config.lock.Lock() - defer Config.lock.Unlock() + if maxSizeInMB > 0 { + Config.lock.Lock() + defer Config.lock.Unlock() - // Parse unit as string to set as environment variable. - strsize := strconv.FormatFloat(maxSizeInMB, 'f', -1, 64) - os.Setenv(LOG_MAX_FILE_SIZE_ENV_VAR, strsize) - Config.MaxFileSizeMB = maxSizeInMB + Config.MaxFileSizeMB = maxSizeInMB + reloadConfig() + } } // SetTimestampFormat sets the time formatting @@ -271,11 +283,12 @@ func SetConfigMaxFileSizeMB(maxSizeInMB float64) { // a valid time format such as time.RFC3339 // or "2006-01-02T15:04:05.000". func SetTimestampFormat(format string) { - Config.lock.Lock() - defer Config.lock.Unlock() - if format != "" { + Config.lock.Lock() + defer Config.lock.Unlock() + Config.timestampFormat = format + reloadConfig() } } @@ -292,6 +305,9 @@ 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() { if err := seelog.RegisterCustomFormatter("EcsAgentLogfmt", logfmtFormatter); err != nil { seelog.Error(err) @@ -303,8 +319,12 @@ func InitSeelog() { seelog.Error(err) } - SetLevel(os.Getenv(LOGLEVEL_ENV_VAR), os.Getenv(LOGLEVEL_ON_INSTANCE_ENV_VAR)) - + if DriverLogLevel := os.Getenv(LOGLEVEL_ENV_VAR); DriverLogLevel != "" { + SetDriverLogLevel(DriverLogLevel) + } + if InstanceLogLevel := os.Getenv(LOGLEVEL_ON_INSTANCE_ENV_VAR); InstanceLogLevel != "" { + SetInstanceLogLevel(InstanceLogLevel) + } if RolloverType := os.Getenv(LOG_ROLLOVER_TYPE_ENV_VAR); RolloverType != "" { Config.RolloverType = RolloverType } diff --git a/ecs-agent/logger/log.go b/ecs-agent/logger/log.go index fc7025f823c..cedcee4c433 100644 --- a/ecs-agent/logger/log.go +++ b/ecs-agent/logger/log.go @@ -44,18 +44,30 @@ const ( DEFAULT_TIMESTAMP_FORMAT = time.RFC3339 DEFAULT_MAX_FILE_SIZE float64 = 10 DEFAULT_MAX_ROLL_COUNT int = 24 + DEFAULT_DUPLICATE_STDOUT = true ) +// logLevels is the mapping from ECS_LOGLEVEL to Seelog provided levels. +var logLevels = map[string]string{ + "debug": "debug", + "info": "info", + "warn": "warn", + "error": "error", + "crit": "critical", + "none": "off", +} + type logConfig struct { - RolloverType string - MaxRollCount int - MaxFileSizeMB float64 - logfile string - driverLevel string - instanceLevel string - outputFormat string - timestampFormat string - lock sync.Mutex + RolloverType string + MaxRollCount int + MaxFileSizeMB float64 + logfile string + driverLevel string + instanceLevel string + outputFormat string + timestampFormat string + duplicateToStdout bool + lock sync.Mutex } var Config *logConfig @@ -148,7 +160,10 @@ func seelogConfig() string { - ` + ` + if Config.duplicateToStdout { + c += `` + } c += platformLogConfig() c += ` ` @@ -159,10 +174,12 @@ func seelogConfig() string { c += ` ` - } else { + } else if Config.RolloverType == "date" { c += ` ` + } else { + c += `` } c += ` ` @@ -191,33 +208,6 @@ func getLevelList(fileLevel string) string { return levelLists[fileLevel] } -// SetLevel sets the log levels for logging -func SetLevel(driverLogLevel, instanceLogLevel string) { - levels := map[string]string{ - "debug": "debug", - "info": "info", - "warn": "warn", - "error": "error", - "crit": "critical", - "none": "off", - } - - parsedDriverLevel, driverOk := levels[strings.ToLower(driverLogLevel)] - parsedInstanceLevel, instanceOk := levels[strings.ToLower(instanceLogLevel)] - - if instanceOk || driverOk { - Config.lock.Lock() - defer Config.lock.Unlock() - if instanceOk { - Config.instanceLevel = parsedInstanceLevel - } - if driverOk { - Config.driverLevel = parsedDriverLevel - } - reloadConfig() - } -} - // GetLevel gets the log level func GetLevel() string { Config.lock.Lock() @@ -236,12 +226,41 @@ func setInstanceLevelDefault() string { return DEFAULT_LOGLEVEL } +// SetInstanceLogLevel explicitly sets the log level for instance logs. +func SetInstanceLogLevel(instanceLogLevel string) { + parsedLevel, ok := logLevels[strings.ToLower(instanceLogLevel)] + if ok { + Config.lock.Lock() + defer Config.lock.Unlock() + + os.Setenv(LOGLEVEL_ON_INSTANCE_ENV_VAR, parsedLevel) + Config.instanceLevel = parsedLevel + reloadConfig() + } +} + +// SetDriverLogLevel explicitly sets the log level for a custom driver. +func SetDriverLogLevel(driverLogLevel string) { + parsedLevel, ok := logLevels[strings.ToLower(driverLogLevel)] + if ok { + Config.lock.Lock() + defer Config.lock.Unlock() + + os.Setenv(LOGLEVEL_ENV_VAR, parsedLevel) + Config.driverLevel = parsedLevel + reloadConfig() + } +} + // SetConfigLogFile sets the default output file of the logger. func SetConfigLogFile(logFile string) { - Config.lock.Lock() - defer Config.lock.Unlock() + if logFile != "" { + Config.lock.Lock() + defer Config.lock.Unlock() - Config.logfile = logFile + Config.logfile = logFile + reloadConfig() + } } // SetConfigLogFormat sets the output format of the logger. @@ -250,20 +269,33 @@ func SetConfigLogFormat(logFormat string) { Config.lock.Lock() defer Config.lock.Unlock() - os.Setenv(LOG_OUTPUT_FORMAT_ENV_VAR, logFormat) Config.outputFormat = logFormat + reloadConfig() } // SetConfigMaxFileSizeMB sets the max file size of a log file // in Megabytes before the logger rotates to a new file. func SetConfigMaxFileSizeMB(maxSizeInMB float64) { - Config.lock.Lock() - defer Config.lock.Unlock() + if maxSizeInMB > 0 { + Config.lock.Lock() + defer Config.lock.Unlock() - // Parse unit as string to set as environment variable. - strsize := strconv.FormatFloat(maxSizeInMB, 'f', -1, 64) - os.Setenv(LOG_MAX_FILE_SIZE_ENV_VAR, strsize) - Config.MaxFileSizeMB = maxSizeInMB + Config.MaxFileSizeMB = maxSizeInMB + reloadConfig() + } +} + +// SetRolloverType sets the logging rollover constraint. +// This should be either size or date. Logger will roll +// to a new log file based on this constraint. +func SetRolloverType(rolloverType string) { + if rolloverType == "date" || rolloverType == "size" { + Config.lock.Lock() + defer Config.lock.Unlock() + + Config.RolloverType = rolloverType + reloadConfig() + } } // SetTimestampFormat sets the time formatting @@ -271,27 +303,43 @@ func SetConfigMaxFileSizeMB(maxSizeInMB float64) { // a valid time format such as time.RFC3339 // or "2006-01-02T15:04:05.000". func SetTimestampFormat(format string) { - Config.lock.Lock() - defer Config.lock.Unlock() - if format != "" { + Config.lock.Lock() + defer Config.lock.Unlock() + Config.timestampFormat = format + reloadConfig() } } +// SetDuplicateToStdout decides whether the logger +// should write to stdout using the tag +// in addition to logfiles that are set up. +func SetDuplicateToStdout(duplicate bool) { + Config.lock.Lock() + defer Config.lock.Unlock() + + Config.duplicateToStdout = duplicate + reloadConfig() +} + func init() { Config = &logConfig{ - logfile: os.Getenv(LOGFILE_ENV_VAR), - driverLevel: DEFAULT_LOGLEVEL, - instanceLevel: setInstanceLevelDefault(), - RolloverType: DEFAULT_ROLLOVER_TYPE, - outputFormat: DEFAULT_OUTPUT_FORMAT, - MaxFileSizeMB: DEFAULT_MAX_FILE_SIZE, - MaxRollCount: DEFAULT_MAX_ROLL_COUNT, - timestampFormat: DEFAULT_TIMESTAMP_FORMAT, + logfile: os.Getenv(LOGFILE_ENV_VAR), + driverLevel: DEFAULT_LOGLEVEL, + instanceLevel: setInstanceLevelDefault(), + RolloverType: DEFAULT_ROLLOVER_TYPE, + outputFormat: DEFAULT_OUTPUT_FORMAT, + MaxFileSizeMB: DEFAULT_MAX_FILE_SIZE, + MaxRollCount: DEFAULT_MAX_ROLL_COUNT, + timestampFormat: DEFAULT_TIMESTAMP_FORMAT, + duplicateToStdout: DEFAULT_DUPLICATE_STDOUT, } } +// 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() { if err := seelog.RegisterCustomFormatter("EcsAgentLogfmt", logfmtFormatter); err != nil { seelog.Error(err) @@ -303,8 +351,12 @@ func InitSeelog() { seelog.Error(err) } - SetLevel(os.Getenv(LOGLEVEL_ENV_VAR), os.Getenv(LOGLEVEL_ON_INSTANCE_ENV_VAR)) - + if DriverLogLevel := os.Getenv(LOGLEVEL_ENV_VAR); DriverLogLevel != "" { + SetDriverLogLevel(DriverLogLevel) + } + if InstanceLogLevel := os.Getenv(LOGLEVEL_ON_INSTANCE_ENV_VAR); InstanceLogLevel != "" { + SetInstanceLogLevel(InstanceLogLevel) + } if RolloverType := os.Getenv(LOG_ROLLOVER_TYPE_ENV_VAR); RolloverType != "" { Config.RolloverType = RolloverType } diff --git a/ecs-agent/logger/log_test.go b/ecs-agent/logger/log_test.go index 2881a0b9b34..590437727f1 100644 --- a/ecs-agent/logger/log_test.go +++ b/ecs-agent/logger/log_test.go @@ -142,7 +142,7 @@ func TestJSONFormat_Structured_Timestamp(t *testing.T) { require.JSONEq(t, `{"level": "debug", "time": "2018-10-01T01:02:03.000", "msg": "This is my log message"}`, s) } -func TestSetLevel(t *testing.T) { +func TestSetLogLevels(t *testing.T) { resetEnv := func() { os.Unsetenv(LOGLEVEL_ENV_VAR) os.Unsetenv(LOGLEVEL_ON_INSTANCE_ENV_VAR) @@ -241,8 +241,9 @@ func TestSetLevel(t *testing.T) { MaxFileSizeMB: DEFAULT_MAX_FILE_SIZE, MaxRollCount: DEFAULT_MAX_ROLL_COUNT, } - SetLevel(os.Getenv(LOGLEVEL_ENV_VAR), os.Getenv(LOGLEVEL_ON_INSTANCE_ENV_VAR)) + SetDriverLogLevel(os.Getenv(LOGLEVEL_ENV_VAR)) require.Equal(t, test.expectedLoglevel, Config.driverLevel) + SetInstanceLogLevel(os.Getenv(LOGLEVEL_ON_INSTANCE_ENV_VAR)) require.Equal(t, test.expectedLoglevelInstance, Config.instanceLevel) }) }