Spinner
Display animated spinners during long-running operations:
err := clog.Spinner("Downloading").
Str("url", fileURL).
Wait(ctx, func(ctx context.Context) error {
return download(ctx, fileURL)
}).
Msg("Downloaded")
The spinner animates with moon phase emojis (ππππππππ) while the action runs, then logs the result. This is the DefaultSpinnerStyle, which is used when no custom Style is set.

Dynamic Status Updates
Use Progress to update the spinner message and fields during execution:
err := clog.Spinner("Processing").
Progress(ctx, func(ctx context.Context, update *clog.ProgressUpdate) error {
for i, item := range items {
update.Msg("Processing").Str("progress", fmt.Sprintf("%d/%d", i+1, len(items))).Send()
if err := process(ctx, item); err != nil {
return err
}
}
return nil
}).
Msg("Processed all items")
WaitResult Finalisers
| Method | Success behaviour | Failure behaviour |
|---|---|---|
.Msg(s) | Logs at INF with message | Logs at ERR with error string |
.Err() | Logs at INF with spinner message | Logs at ERR with error string as msg |
.Send() | Logs at configured level | Logs at configured level |
.Silent() | Returns error, no logging | Returns error, no logging |
.Err() is equivalent to calling .Send() with default settings (no OnSuccess/OnError overrides).
All finalisers return the error from the action. You can chain any field method (.Str(), .Int(), .Bool(), .Duration(), etc.) and .Prefix() on a WaitResult before finalising.
Custom Success/Error Behaviour
Use OnSuccessLevel, OnSuccessMessage, OnErrorLevel, and OnErrorMessage to customise how the result is logged, then call .Send():
// Fatal on error instead of the default error level
err := clog.Spinner("Connecting to database").
Str("host", "db.internal").
Wait(ctx, connectToDB).
OnErrorLevel(clog.FatalLevel).
Send()
When OnErrorMessage is set, the custom message becomes the log message and the original error is included as an error= field. Without it, the error string is used directly as the message with no extra field.
Custom Spinner Style
clog.Spinner("Loading").
Style(clog.SpinnerDot).
Wait(ctx, action).
Msg("Done")
See progress_spinner_presets.go for the full list of available spinner types.
Hyperlink Fields on Animations
The AnimationBuilder supports the same clickable hyperlink field methods as events:
clog.Spinner("Building").
Path("dir", "src/").
Line("config", "config.yaml", 42).
Column("loc", "main.go", 10, 5).
URL("docs", "https://example.com").
Link("help", "https://example.com", "docs").
Wait(ctx, action).
Msg("Built")
Elapsed Timer
Add a live elapsed-time field to any animation with .Elapsed(key):
err := clog.Spinner("Processing batch").
Str("batch", "1/3").
Elapsed("elapsed").
Int("workers", 4).
Wait(ctx, processBatch).
Msg("Batch processed")
// INF β
Batch processed batch=1/3 elapsed=2s workers=4
The elapsed field respects its position relative to other field methods - it appears between batch and workers in the output above because .Elapsed("elapsed") was called between .Str() and .Int().
The display format uses SetElapsedPrecision (default 0 decimal places), rounds to SetElapsedRound (default 1s), hides values below SetElapsedMinimum (default 1s), and can be fully overridden with SetElapsedFormatFunc. Durations >= 1m use composite format (e.g. β1m30sβ, β2h15mβ).
Per-Event Parts Override
Override the part order for a spinner and its completion message without mutating the logger:
err := clog.Spinner("Indexing files").
Parts(clog.PartPrefix, clog.PartMessage).
Wait(ctx, indexFiles).
Msg("Indexed")
// β
Indexed (no level label or fields)
When set on the AnimationBuilder, the override applies to both the animation rendering and the default completion message. You can further override on the WaitResult if the completion needs different parts:
clog.Spinner("Syncing").
Parts(clog.PartMessage). // animation: message only
Wait(ctx, sync).
Parts(clog.PartLevel, clog.PartMessage). // completion: add level back
Msg("Synced")
Delayed Animation
Use .After(d) to suppress the animation for an initial duration. If the task finishes before the delay, no animation is shown at all - useful for operations that are usually fast but occasionally slow:
err := clog.Spinner("Fetching config").
After(time.Second).
Wait(ctx, fetchConfig).
Msg("Config loaded")
If fetchConfig completes in under 1 second, the user sees nothing until the final βConfig loadedβ message. If it takes longer, the spinner appears after 1 second.