You've already forked watchtower
							
							
				mirror of
				https://github.com/containrrr/watchtower.git
				synced 2025-10-31 00:17:44 +02:00 
			
		
		
		
	feat: allow logging output to use JSON formatter (#1705)
Co-authored-by: nils måsén <nils@piksel.se>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							897b1714d0
						
					
				
				
					commit
					79ebad0e19
				
			
							
								
								
									
										22
									
								
								cmd/root.go
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								cmd/root.go
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"math" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| @@ -78,23 +79,8 @@ func Execute() { | ||||
| func PreRun(cmd *cobra.Command, _ []string) { | ||||
| 	f := cmd.PersistentFlags() | ||||
| 	flags.ProcessFlagAliases(f) | ||||
|  | ||||
| 	if enabled, _ := f.GetBool("no-color"); enabled { | ||||
| 		log.SetFormatter(&log.TextFormatter{ | ||||
| 			DisableColors: true, | ||||
| 		}) | ||||
| 	} else { | ||||
| 		// enable logrus built-in support for https://bixense.com/clicolors/ | ||||
| 		log.SetFormatter(&log.TextFormatter{ | ||||
| 			EnvironmentOverrideColors: true, | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	rawLogLevel, _ := f.GetString(`log-level`) | ||||
| 	if logLevel, err := log.ParseLevel(rawLogLevel); err != nil { | ||||
| 		log.Fatalf("Invalid log level: %s", err.Error()) | ||||
| 	} else { | ||||
| 		log.SetLevel(logLevel) | ||||
| 	if err := flags.SetupLogging(f); err != nil { | ||||
| 		log.Fatalf("Failed to initialize logging: %s", err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	scheduleSpec, _ = f.GetString("schedule") | ||||
| @@ -201,7 +187,7 @@ func Run(c *cobra.Command, names []string) { | ||||
| 		httpAPI.RegisterHandler(metricsHandler.Path, metricsHandler.Handle) | ||||
| 	} | ||||
|  | ||||
| 	if err := httpAPI.Start(enableUpdateAPI && !unblockHTTPAPI); err != nil && err != http.ErrServerClosed { | ||||
| 	if err := httpAPI.Start(enableUpdateAPI && !unblockHTTPAPI); err != nil && !errors.Is(err, http.ErrServerClosed) { | ||||
| 		log.Error("failed to start API", err) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -134,6 +134,17 @@ Environment Variable: WATCHTOWER_LOG_LEVEL | ||||
|              Default: info | ||||
| ``` | ||||
|  | ||||
| ## Logging format | ||||
|  | ||||
| Sets what logging format to use for console output. | ||||
|  | ||||
| ```text | ||||
|             Argument: --log-format, -l | ||||
| Environment Variable: WATCHTOWER_LOG_FORMAT | ||||
|      Possible values: Auto, LogFmt, Pretty or JSON | ||||
|              Default: Auto | ||||
| ``` | ||||
|  | ||||
| ## ANSI colors | ||||
| Disable ANSI color escape codes in log output. | ||||
|  | ||||
| @@ -407,6 +418,7 @@ Environment Variable: WATCHTOWER_WARN_ON_HEAD_FAILURE | ||||
|      Possible values: always, auto, never | ||||
|              Default: auto | ||||
| ``` | ||||
|  | ||||
| ## Programatic Output (porcelain) | ||||
|  | ||||
| Writes the session results to STDOUT using a stable, machine-readable format (indicated by the argument VERSION).   | ||||
|   | ||||
| @@ -85,6 +85,12 @@ func RegisterSystemFlags(rootCmd *cobra.Command) { | ||||
| 		envBool("WATCHTOWER_LABEL_ENABLE"), | ||||
| 		"Watch containers where the com.centurylinklabs.watchtower.enable label is true") | ||||
|  | ||||
| 	flags.StringP( | ||||
| 		"log-format", | ||||
| 		"l", | ||||
| 		viper.GetString("WATCHTOWER_LOG_FORMAT"), | ||||
| 		"Sets what logging format to use for console output. Possible values: Auto, LogFmt, Pretty, JSON") | ||||
|  | ||||
| 	flags.BoolP( | ||||
| 		"debug", | ||||
| 		"d", | ||||
| @@ -409,6 +415,7 @@ func SetDefaults() { | ||||
| 	viper.SetDefault("WATCHTOWER_NOTIFICATION_EMAIL_SUBJECTTAG", "") | ||||
| 	viper.SetDefault("WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER", "watchtower") | ||||
| 	viper.SetDefault("WATCHTOWER_LOG_LEVEL", "info") | ||||
| 	viper.SetDefault("WATCHTOWER_LOG_FORMAT", "auto") | ||||
| } | ||||
|  | ||||
| // EnvConfig translates the command-line options into environment variables | ||||
| @@ -611,6 +618,46 @@ func ProcessFlagAliases(flags *pflag.FlagSet) { | ||||
|  | ||||
| } | ||||
|  | ||||
| // SetupLogging reads only the flags that is needed to set up logging and applies them to the global logger | ||||
| func SetupLogging(f *pflag.FlagSet) error { | ||||
| 	logFormat, _ := f.GetString(`log-format`) | ||||
| 	noColor, _ := f.GetBool("no-color") | ||||
|  | ||||
| 	switch strings.ToLower(logFormat) { | ||||
| 	case "auto": | ||||
| 		// This will either use the "pretty" or "logfmt" format, based on whether the standard out is connected to a TTY | ||||
| 		log.SetFormatter(&log.TextFormatter{ | ||||
| 			DisableColors: noColor, | ||||
| 			// enable logrus built-in support for https://bixense.com/clicolors/ | ||||
| 			EnvironmentOverrideColors: true, | ||||
| 		}) | ||||
| 	case "json": | ||||
| 		log.SetFormatter(&log.JSONFormatter{}) | ||||
| 	case "logfmt": | ||||
| 		log.SetFormatter(&log.TextFormatter{ | ||||
| 			DisableColors: true, | ||||
| 			FullTimestamp: true, | ||||
| 		}) | ||||
| 	case "pretty": | ||||
| 		log.SetFormatter(&log.TextFormatter{ | ||||
| 			// "Pretty" format combined with `--no-color` will only change the timestamp to the time since start | ||||
| 			ForceColors:   !noColor, | ||||
| 			FullTimestamp: false, | ||||
| 		}) | ||||
| 	default: | ||||
| 		return fmt.Errorf("invalid log format: %s", logFormat) | ||||
| 	} | ||||
|  | ||||
| 	rawLogLevel, _ := f.GetString(`log-level`) | ||||
| 	if logLevel, err := log.ParseLevel(rawLogLevel); err != nil { | ||||
| 		return fmt.Errorf("invalid log level: %e", err) | ||||
| 	} else { | ||||
| 		log.SetLevel(logLevel) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func flagIsEnabled(flags *pflag.FlagSet, name string) bool { | ||||
| 	value, err := flags.GetBool(name) | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -177,6 +177,57 @@ func TestProcessFlagAliasesLogLevelFromEnvironment(t *testing.T) { | ||||
| 	assert.Equal(t, `debug`, logLevel) | ||||
| } | ||||
|  | ||||
| func TestLogFormatFlag(t *testing.T) { | ||||
| 	cmd := new(cobra.Command) | ||||
|  | ||||
| 	SetDefaults() | ||||
| 	RegisterDockerFlags(cmd) | ||||
| 	RegisterSystemFlags(cmd) | ||||
|  | ||||
| 	// Ensure the default value is Auto | ||||
| 	require.NoError(t, cmd.ParseFlags([]string{})) | ||||
| 	require.NoError(t, SetupLogging(cmd.Flags())) | ||||
| 	assert.IsType(t, &logrus.TextFormatter{}, logrus.StandardLogger().Formatter) | ||||
|  | ||||
| 	// Test JSON format | ||||
| 	require.NoError(t, cmd.ParseFlags([]string{`--log-format`, `JSON`})) | ||||
| 	require.NoError(t, SetupLogging(cmd.Flags())) | ||||
| 	assert.IsType(t, &logrus.JSONFormatter{}, logrus.StandardLogger().Formatter) | ||||
|  | ||||
| 	// Test Pretty format | ||||
| 	require.NoError(t, cmd.ParseFlags([]string{`--log-format`, `pretty`})) | ||||
| 	require.NoError(t, SetupLogging(cmd.Flags())) | ||||
| 	assert.IsType(t, &logrus.TextFormatter{}, logrus.StandardLogger().Formatter) | ||||
| 	textFormatter, ok := (logrus.StandardLogger().Formatter).(*logrus.TextFormatter) | ||||
| 	assert.True(t, ok) | ||||
| 	assert.True(t, textFormatter.ForceColors) | ||||
| 	assert.False(t, textFormatter.FullTimestamp) | ||||
|  | ||||
| 	// Test LogFmt format | ||||
| 	require.NoError(t, cmd.ParseFlags([]string{`--log-format`, `logfmt`})) | ||||
| 	require.NoError(t, SetupLogging(cmd.Flags())) | ||||
| 	textFormatter, ok = (logrus.StandardLogger().Formatter).(*logrus.TextFormatter) | ||||
| 	assert.True(t, ok) | ||||
| 	assert.True(t, textFormatter.DisableColors) | ||||
| 	assert.True(t, textFormatter.FullTimestamp) | ||||
|  | ||||
| 	// Test invalid format | ||||
| 	require.NoError(t, cmd.ParseFlags([]string{`--log-format`, `cowsay`})) | ||||
| 	require.Error(t, SetupLogging(cmd.Flags())) | ||||
| } | ||||
|  | ||||
| func TestLogLevelFlag(t *testing.T) { | ||||
| 	cmd := new(cobra.Command) | ||||
|  | ||||
| 	SetDefaults() | ||||
| 	RegisterDockerFlags(cmd) | ||||
| 	RegisterSystemFlags(cmd) | ||||
|  | ||||
| 	// Test invalid format | ||||
| 	require.NoError(t, cmd.ParseFlags([]string{`--log-level`, `gossip`})) | ||||
| 	require.Error(t, SetupLogging(cmd.Flags())) | ||||
| } | ||||
|  | ||||
| func TestProcessFlagAliasesSchedAndInterval(t *testing.T) { | ||||
| 	logrus.StandardLogger().ExitFunc = func(_ int) { panic(`FATAL`) } | ||||
| 	cmd := new(cobra.Command) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user