From 10a654becbaf28e098d3f539229f01c4e629c057 Mon Sep 17 00:00:00 2001 From: Hugh Date: Wed, 28 Jan 2026 20:25:39 -0800 Subject: [PATCH] feat: add log-local-time flag for local timezone in logs Add a --log-local-time flag that configures cloudflared to use local timezone for log timestamps instead of the default UTC. This helps users correlate logs with local system events and other services running in their timezone. The flag can also be set via the TUNNEL_LOG_LOCAL_TIME environment variable. Closes #1530 --- cmd/cloudflared/cliutil/logger.go | 6 ++++++ cmd/cloudflared/flags/flags.go | 3 +++ cmd/cloudflared/tunnel/cmd.go | 1 + logger/create.go | 12 ++++++++++++ logger/create_test.go | 26 ++++++++++++++++++++++++++ 5 files changed, 48 insertions(+) diff --git a/cmd/cloudflared/cliutil/logger.go b/cmd/cloudflared/cliutil/logger.go index 0fb4a54b..156a1f65 100644 --- a/cmd/cloudflared/cliutil/logger.go +++ b/cmd/cloudflared/cliutil/logger.go @@ -54,6 +54,12 @@ func ConfigureLoggingFlags(shouldHide bool) []cli.Flag { EnvVars: []string{"TUNNEL_TRACE_OUTPUT"}, Hidden: shouldHide, }), + altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: flags.LogLocalTime, + Usage: "Use local timezone for log timestamps instead of UTC.", + EnvVars: []string{"TUNNEL_LOG_LOCAL_TIME"}, + Hidden: shouldHide, + }), FlagLogOutput, } } diff --git a/cmd/cloudflared/flags/flags.go b/cmd/cloudflared/flags/flags.go index 63af1dd8..4ec6758c 100644 --- a/cmd/cloudflared/flags/flags.go +++ b/cmd/cloudflared/flags/flags.go @@ -143,6 +143,9 @@ const ( LogFormatOutputValueDefault = "default" LogFormatOutputValueJSON = "json" + // LogLocalTime uses local timezone instead of UTC for log timestamps. + LogLocalTime = "log-local-time" + // TraceOutput is the command line flag to set the name of trace output file TraceOutput = "trace-output" diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 4bd08dc2..7f1053b4 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -115,6 +115,7 @@ var ( cfdflags.LogFile, cfdflags.LogDirectory, cfdflags.TraceOutput, + cfdflags.LogLocalTime, cfdflags.ProxyDns, "proxy-dns-port", "proxy-dns-address", diff --git a/logger/create.go b/logger/create.go index 2ae67ab9..460a0b6f 100644 --- a/logger/create.go +++ b/logger/create.go @@ -44,6 +44,16 @@ func utcNow() time.Time { return time.Now().UTC() } +func localNow() time.Time { + return time.Now() +} + +func configureTimestampFunc(useLocalTime bool) { + if useLocalTime { + zerolog.TimestampFunc = localNow + } +} + func fallbackLogger(err error) *zerolog.Logger { failLog := fallbacklog.With().Logger() fallbacklog.Error().Msgf("Falling back to a default logger due to logger setup failure: %s", err) @@ -146,6 +156,8 @@ func createFromContext( logDirectoryFlagName string, disableTerminal bool, ) *zerolog.Logger { + configureTimestampFunc(c.Bool(cfdflags.LogLocalTime)) + logLevel := c.String(logLevelFlagName) logFile := c.String(cfdflags.LogFile) logDirectory := c.String(logDirectoryFlagName) diff --git a/logger/create_test.go b/logger/create_test.go index 2e0f9b96..82049978 100644 --- a/logger/create_test.go +++ b/logger/create_test.go @@ -3,6 +3,7 @@ package logger import ( "io" "testing" + "time" "github.com/pkg/errors" "github.com/rs/zerolog" @@ -130,3 +131,28 @@ func TestResilientMultiWriter_Management(t *testing.T) { }) } } + +func TestTimestampFunctions(t *testing.T) { + t.Run("utcNow returns UTC", func(t *testing.T) { + ts := utcNow() + _, offset := ts.Zone() + assert.Equal(t, 0, offset) + }) + + t.Run("localNow returns local timezone", func(t *testing.T) { + ts := localNow() + expected := time.Now() + _, expectedOffset := expected.Zone() + _, actualOffset := ts.Zone() + assert.Equal(t, expectedOffset, actualOffset) + }) + + t.Run("configureTimestampFunc sets local time when true", func(t *testing.T) { + configureTimestampFunc(true) + ts := zerolog.TimestampFunc() + expected := time.Now() + _, expectedOffset := expected.Zone() + _, actualOffset := ts.Zone() + assert.Equal(t, expectedOffset, actualOffset) + }) +}