Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Elapsed

Measure and log the elapsed duration of an operation without animations:

e := clog.Info().Elapsed("elapsed")
runMigrations()
e.Msg("database migration")
// INF ℹ️ database migration elapsed=2s

Use Elapsed when you want elapsed-time logging but don’t need a spinner, progress bar, or any visual animation. The elapsed field uses the same formatting, styling, and field type as animation Elapsed fields.

Finalising

Elapsed events can be finalised with Send (no message), Msg, or Msgf:

e := clog.Info().Elapsed("elapsed")
runMigrations()
e.Send()
// INF ℹ️ elapsed=2s

e = clog.Info().Elapsed("elapsed")
runMigrations()
e.Msg("migrations complete")
// INF ℹ️ migrations complete elapsed=2s

Field Positioning

Elapsed can appear anywhere in the field chain to control where the elapsed field is shown:

// Elapsed before other fields
e := clog.Info().Elapsed("elapsed").Str("env", "prod")
deploy()
e.Msg("deploy")
// INF ℹ️ deploy elapsed=12s env=prod

// Elapsed after other fields
e = clog.Info().Str("env", "prod").Elapsed("elapsed")
deploy()
e.Msg("deploy")
// INF ℹ️ deploy env=prod elapsed=12s

On animated builders (fx.Builder), fields added at runtime via a live update always render after the builder’s base fields - use elapsed.Trailing() to pin the elapsed field to the end of the row regardless (see Spinner).

Custom Key

The key parameter controls the field name:

e := clog.Info().Elapsed("duration")
compile()
e.Msg("compile")
// INF ℹ️ compile duration=3s

Log Levels

Since Elapsed is a method on events, you control the level directly:

clog.Warn().Elapsed("elapsed").Msg("slow query")
// WRN ⚠️ slow query elapsed=5s

clog.Error().Elapsed("elapsed").Err(err).Msg("compile")
// ERR ❌ compile elapsed=3s error="syntax error"

Sub-loggers

Elapsed works on any logger instance:

logger := clog.With().Str("component", "db").Logger()
e := logger.Info().Elapsed("elapsed")
runQuery()
e.Msg("query")
// INF ℹ️ query component=db elapsed=1s

Elapsed Configuration

The elapsed field respects the same per-logger FieldFormats configuration as animation elapsed fields:

FieldDefaultDescription
DurationFormatnil (built-in)Custom formatter for both Duration and Elapsed fields
DurationGradientMax0 (disabled)Max duration for Duration field gradient (see Styles)
DurationMinimumtime.SecondHide Duration fields below this threshold (0 shows all values)
DurationPrecision0Decimal places for Duration fields (0 = 3s, 1 = 3.2s)
DurationRoundtime.SecondRounding granularity for Duration fields (0 disables rounding)
ElapsedFormatnil (built-in)Custom formatter for elapsed durations (takes priority over DurationFormat)
ElapsedGradientMax0 (disabled)Max duration for gradient coloring (see Styles)
ElapsedMinimumtime.SecondHide elapsed field below this threshold (0 shows all values)
ElapsedPrecision0Decimal places (0 = 3s, 1 = 3.2s)
ElapsedRoundtime.SecondRounding granularity (0 disables rounding)

ElapsedGradientMax, ElapsedMinimum, and the ElapsedGradient/ElapsedGradientMode style settings, can also be overridden per field with options from the elapsed package - see Per-Field Overrides. The duration package mirrors this for Duration fields, including duration.WithMinimum:

import "github.com/gechr/clog/field/elapsed"

e := clog.Info().Elapsed("elapsed",
  elapsed.WithGradientMax(5*time.Second),
  elapsed.WithMinimum(0), // always show this field, regardless of the logger default
)
runMigrations()
e.Msg("database migration")

Duration Format Function

DurationFormat configures a single format function that applies to both Duration fields and Elapsed fields. This is useful when you have a shared helper (e.g. from a utility package) that you want applied consistently across all duration logging:

f := clog.DefaultFieldFormats()
f.DurationFormat = commonutil.FormatDuration
clog.SetFieldFormats(f)

// Both of these now use commonutil.FormatDuration:
clog.Info().Duration("took", time.Since(start)).Msg("done")
// INF ℹ️ done took=2.3s

e := clog.Info().Elapsed("elapsed")
doWork()
e.Msg("done")
// INF ℹ️ done elapsed=2.3s

When ElapsedFormat is also set, it takes priority over DurationFormat for Elapsed fields only. Duration fields always use DurationFormat:

f := clog.DefaultFieldFormats()
f.DurationFormat = func(d time.Duration) string { return "dur:" + d.String() }
f.ElapsedFormat = func(d time.Duration) string { return "ela:" + d.String() }
clog.SetFieldFormats(f)

clog.Info().Duration("latency", 3*time.Second).Msg("request")
// INF ℹ️ request latency=dur:3s  ← uses DurationFormat

e := clog.Info().Elapsed("elapsed")
e.Msg("done")
// INF ℹ️ done elapsed=ela:3s    ← ElapsedFormat takes priority