Repository: buchgr/bazel-remote
Pull request #619 (2022-12-11 22:11:46)

feature: allow setting local timezone for logs (v2)
This is #566 with some small fixes, and also adds a `--log_timezone none` value to disable timestamps in logs (which is useful when running bazel-remote as a systemd service, since systemd tracks the timetsamps itself).

Thanks to @shubhindia for implementing this feature.


Patch:
diff --git a/README.md b/README.md
index 9747cb6..661a555 100644
--- a/README.md
+++ b/README.md
@@ -366,6 +366,10 @@ OPTIONS:
       must be one of "none" or "all". (default: all, ie enable full access
       logging) [$BAZEL_REMOTE_ACCESS_LOG_LEVEL]
 
+   --log_timezone value The timezone to use for log timestamps. If supplied,
+      must be one of "UTC", "local" or "none" for no timestamps. (default: UTC,
+      ie use UTC timezone) [$BAZEL_REMOTE_LOG_TIMEZONE]
+
    --help, -h  show help (default: false)
 ```
 
@@ -509,6 +513,9 @@ http_address: 0.0.0.0:8080
 
 # If supplied, controls the verbosity of the access logger ("none" or "all"):
 #access_log_level: none
+
+# If supplied, controls the timezone of the access logger ("UTC", "local" or "none"):
+#log_timezone: local
 ```
 
 ## Docker
diff --git a/config/config.go b/config/config.go
index d330742..c2509d3 100644
--- a/config/config.go
+++ b/config/config.go
@@ -64,6 +64,7 @@ type Config struct {
 	HTTPReadTimeout             time.Duration             `yaml:"http_read_timeout"`
 	HTTPWriteTimeout            time.Duration             `yaml:"http_write_timeout"`
 	AccessLogLevel              string                    `yaml:"access_log_level"`
+	LogTimezone                 string                    `yaml:"log_timezone"`
 	MaxBlobSize                 int64                     `yaml:"max_blob_size"`
 	MaxProxyBlobSize            int64                     `yaml:"max_proxy_blob_size"`
 
@@ -117,6 +118,7 @@ func newFromArgs(dir string, maxSize int, storageMode string, zstdImplementation
 	httpReadTimeout time.Duration,
 	httpWriteTimeout time.Duration,
 	accessLogLevel string,
+	logTimezone string,
 	maxBlobSize int64,
 	maxProxyBlobSize int64) (*Config, error) {
 
@@ -149,6 +151,7 @@ func newFromArgs(dir string, maxSize int, storageMode string, zstdImplementation
 		HTTPReadTimeout:             httpReadTimeout,
 		HTTPWriteTimeout:            httpWriteTimeout,
 		AccessLogLevel:              accessLogLevel,
+		LogTimezone:                 logTimezone,
 		MaxBlobSize:                 maxBlobSize,
 		MaxProxyBlobSize:            maxProxyBlobSize,
 	}
@@ -189,6 +192,7 @@ func newFromYaml(data []byte) (*Config, error) {
 			MaxProxyBlobSize:       math.MaxInt64,
 			MetricsDurationBuckets: defaultDurationBuckets,
 			AccessLogLevel:         "all",
+			LogTimezone:            "UTC",
 		},
 	}
 
@@ -378,6 +382,12 @@ func validateConfig(c *Config) error {
 		return errors.New("'access_log_level' must be set to either \"none\" or \"all\"")
 	}
 
+	switch c.LogTimezone {
+	case "UTC", "local", "none":
+	default:
+		return errors.New("'log_timezone' must be set to either \"UTC\", \"local\" or \"none\"")
+	}
+
 	return nil
 }
 
@@ -510,6 +520,7 @@ func get(ctx *cli.Context) (*Config, error) {
 		ctx.Duration("http_read_timeout"),
 		ctx.Duration("http_write_timeout"),
 		ctx.String("access_log_level"),
+		ctx.String("log_timezone"),
 		ctx.Int64("max_blob_size"),
 		ctx.Int64("max_proxy_blob_size"),
 	)
diff --git a/config/logger.go b/config/logger.go
index 4779dc5..8070f76 100644
--- a/config/logger.go
+++ b/config/logger.go
@@ -6,13 +6,22 @@ import (
 	"os"
 )
 
-const (
-	LogFlags = log.Ldate | log.Ltime | log.LUTC
-)
-
 func (c *Config) setLogger() error {
-	c.AccessLogger = log.New(os.Stdout, "", LogFlags)
-	c.ErrorLogger = log.New(os.Stderr, "", LogFlags)
+
+	var logFlags int
+	switch c.LogTimezone {
+	case "UTC":
+		logFlags = log.Ldate | log.Ltime | log.LUTC
+	case "local":
+		logFlags = log.Ldate | log.Ltime
+	case "none":
+		logFlags = 0
+	}
+
+	log.SetFlags(logFlags)
+
+	c.AccessLogger = log.New(os.Stdout, "", logFlags)
+	c.ErrorLogger = log.New(os.Stderr, "", logFlags)
 
 	if c.AccessLogLevel == "none" {
 		c.AccessLogger.SetOutput(io.Discard)
diff --git a/main.go b/main.go
index 86ac23a..b20260c 100644
--- a/main.go
+++ b/main.go
@@ -42,15 +42,6 @@ import (
 var gitCommit string
 
 func main() {
-	log.SetFlags(config.LogFlags)
-
-	maybeGitCommitMsg := ""
-	if len(gitCommit) > 0 && gitCommit != "{STABLE_GIT_COMMIT}" {
-		maybeGitCommitMsg = fmt.Sprintf(" from git commit %s", gitCommit)
-	}
-	log.Printf("bazel-remote built with %s%s.",
-		runtime.Version(), maybeGitCommitMsg)
-
 	app := cli.NewApp()
 
 	cli.AppHelpTemplate = flags.Template
@@ -87,6 +78,13 @@ func run(ctx *cli.Context) error {
 		os.Exit(1)
 	}
 
+	maybeGitCommitMsg := ""
+	if len(gitCommit) > 0 && gitCommit != "{STABLE_GIT_COMMIT}" {
+		maybeGitCommitMsg = fmt.Sprintf(" from git commit %s", gitCommit)
+	}
+	log.Printf("bazel-remote built with %s%s.",
+		runtime.Version(), maybeGitCommitMsg)
+
 	rlimit.Raise()
 
 	grpcSem := semaphore.NewWeighted(1)
diff --git a/utils/flags/flags.go b/utils/flags/flags.go
index b24330f..02ba4be 100644
--- a/utils/flags/flags.go
+++ b/utils/flags/flags.go
@@ -382,5 +382,12 @@ func GetCliFlags() []cli.Flag {
 			DefaultText: "all, ie enable full access logging",
 			EnvVars:     []string{"BAZEL_REMOTE_ACCESS_LOG_LEVEL"},
 		},
+		&cli.StringFlag{
+			Name:        "log_timezone",
+			Usage:       "The timezone to use for log timestamps. If supplied, must be one of \"UTC\", \"local\" or \"none\" for no timestamps.",
+			Value:       "UTC",
+			DefaultText: "UTC, ie use UTC timezone",
+			EnvVars:     []string{"BAZEL_REMOTE_LOG_TIMEZONE"},
+		},
 	}
 }


Test patch:
diff --git a/config/config_test.go b/config/config_test.go
index 63c0515..eb33ec6 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -27,6 +27,7 @@ experimental_remote_asset_api: true
 http_read_timeout: 5s
 http_write_timeout: 10s
 access_log_level: none
+log_timezone: local
 `
 
 	config, err := newFromYaml([]byte(yaml))
@@ -56,6 +57,7 @@ access_log_level: none
 		MaxProxyBlobSize:            math.MaxInt64,
 		MetricsDurationBuckets:      []float64{.5, 1, 2.5, 5, 10, 20, 40, 80, 160, 320},
 		AccessLogLevel:              "none",
+		LogTimezone:                 "local",
 	}
 
 	if !reflect.DeepEqual(config, expectedConfig) {
@@ -97,6 +99,7 @@ gcs_proxy:
 		MaxProxyBlobSize:       math.MaxInt64,
 		MetricsDurationBuckets: []float64{.5, 1, 2.5, 5, 10, 20, 40, 80, 160, 320},
 		AccessLogLevel:         "all",
+		LogTimezone:            "UTC",
 	}
 
 	if !cmp.Equal(config, expectedConfig) {
@@ -135,6 +138,7 @@ http_proxy:
 		MaxProxyBlobSize:       math.MaxInt64,
 		MetricsDurationBuckets: []float64{.5, 1, 2.5, 5, 10, 20, 40, 80, 160, 320},
 		AccessLogLevel:         "all",
+		LogTimezone:            "UTC",
 	}
 
 	if !cmp.Equal(config, expectedConfig) {
@@ -210,6 +214,7 @@ s3_proxy:
 		MaxProxyBlobSize:       math.MaxInt64,
 		MetricsDurationBuckets: []float64{.5, 1, 2.5, 5, 10, 20, 40, 80, 160, 320},
 		AccessLogLevel:         "all",
+		LogTimezone:            "UTC",
 	}
 
 	if !cmp.Equal(config, expectedConfig) {
@@ -242,6 +247,7 @@ profile_address: :7070
 		MaxProxyBlobSize:       math.MaxInt64,
 		MetricsDurationBuckets: []float64{.5, 1, 2.5, 5, 10, 20, 40, 80, 160, 320},
 		AccessLogLevel:         "all",
+		LogTimezone:            "UTC",
 	}
 
 	if !cmp.Equal(config, expectedConfig) {
@@ -288,6 +294,7 @@ endpoint_metrics_duration_buckets: [.005, .1, 5]
 		MaxProxyBlobSize:       math.MaxInt64,
 		MetricsDurationBuckets: []float64{0.005, 0.1, 5},
 		AccessLogLevel:         "all",
+		LogTimezone:            "UTC",
 	}
 
 	if !cmp.Equal(config, expectedConfig) {
@@ -418,6 +425,7 @@ storage_mode: zstd
 		MaxProxyBlobSize:       math.MaxInt64,
 		MetricsDurationBuckets: []float64{.5, 1, 2.5, 5, 10, 20, 40, 80, 160, 320},
 		AccessLogLevel:         "all",
+		LogTimezone:            "UTC",
 	}
 
 	if !cmp.Equal(config, expectedConfig) {
@@ -450,6 +458,7 @@ storage_mode: zstd
 		MaxProxyBlobSize:       math.MaxInt64,
 		MetricsDurationBuckets: []float64{.5, 1, 2.5, 5, 10, 20, 40, 80, 160, 320},
 		AccessLogLevel:         "all",
+		LogTimezone:            "UTC",
 	}
 
 	if !cmp.Equal(config, expectedConfig) {