clog

package module
v0.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 4, 2026 License: MIT Imports: 35 Imported by: 2

README

clog

A highly customizable structured logger for command-line tools with a zerolog-inspired fluent API, terminal-aware colors, hyperlinks, animations, and custom log levels.

Demo

clog demo

Installation

go get github.com/gechr/clog

Quick Start

package main

import (
  "fmt"

  "github.com/gechr/clog"
)

func main() {
  clog.Info().Str("port", "8080").Msg("Server started")
  clog.Warn().Str("path", "/old").Msg("Deprecated endpoint")
  err := fmt.Errorf("connection refused")
  clog.Error().Err(err).Msg("Connection failed")
}

Output:

INF ℹ️ Server started port=8080
WRN ⚠️ Deprecated endpoint path=/old
ERR ❌ Connection failed error=connection refused

Documentation

Full documentation is available at gechr.github.io/clog.

Documentation

Overview

Package clog provides structured CLI logging with terminal-aware colors, hyperlinks, and spinners.

It uses a zerolog-style fluent API for building log entries:

clog.Info().Str("port", "8080").Msg("Server started")

The default output is a pretty terminal formatter. A custom Handler can be set for alternative formats (e.g. JSON).

Index

Constants

View Source
const (
	LevelTrace = level.Trace
	LevelDebug = level.Debug
	LevelInfo  = level.Info
	LevelDry   = level.Dry
	LevelWarn  = level.Warn
	LevelError = level.Error
	LevelFatal = level.Fatal

	// UnsetLevel is passed to [SetNonTTYLevel] to disable the non-TTY level
	// filter. Its value is intentionally below all real log levels so the
	// check e.level < nonTTYLevel is always false, meaning no restriction.
	UnsetLevel = level.Unset
)
View Source
const (
	LevelTraceValue = level.TraceValue
	LevelDebugValue = level.DebugValue
	LevelInfoValue  = level.InfoValue
	LevelDryValue   = level.DryValue
	LevelWarnValue  = level.WarnValue
	LevelErrorValue = level.ErrorValue
	LevelFatalValue = level.FatalValue
)
View Source
const (
	QuoteAuto   = core.QuoteAuto
	QuoteAlways = core.QuoteAlways
	QuoteNever  = core.QuoteNever
)
View Source
const (
	SortNone       = core.SortNone
	SortAscending  = core.SortAscending
	SortDescending = core.SortDescending
)
View Source
const (
	PartTimestamp = core.PartTimestamp
	PartLevel     = core.PartLevel
	PartSymbol    = core.PartSymbol
	PartMessage   = core.PartMessage
	PartFields    = core.PartFields
)
View Source
const (
	TreeFirst  = core.TreeFirst
	TreeMiddle = core.TreeMiddle
	TreeLast   = core.TreeLast
)
View Source
const DefaultEnvPrefix = "CLOG"

DefaultEnvPrefix is the default environment variable prefix.

View Source
const ErrorKey = core.ErrorKey

ErrorKey is the default field key used by Event.Err and [Context.Err].

View Source
const Nil = core.Nil

Nil is the string representation used for nil values.

Variables

View Source
var Default = New(Stdout(ColorAuto))

Default is the default logger instance.

Functions

func Bar added in v0.5.0

func Bar(msg string, total int, opts ...bar.Option) *fx.Builder

Bar creates a new fx.Builder using the Default logger with a determinate progress bar animation. total is the maximum progress value. Use [Update.SetProgress] to update progress.

func ColorsDisabled

func ColorsDisabled() bool

ColorsDisabled returns true if colors are disabled on the Default logger.

func Configure

func Configure(cfg *Config)

Configure sets up the Default logger with the given configuration. Call this once at application startup.

Note: this respects the log level environment variable - it won't reset the level if CLOG_LOG_LEVEL (or a custom prefix equivalent) was set and cfg.Verbose is false.

func DefaultStyles

func DefaultStyles() *style.Config

DefaultStyles returns the default color styles.

func Group added in v0.5.0

func Group(ctx context.Context) *fx.Group

Group creates a new animation group using the Default logger.

func Hyperlink(url, text string) string

Hyperlink wraps text in an OSC 8 terminal hyperlink escape sequence. Returns plain text when colors or hyperlinks are disabled globally.

func IsTerminal

func IsTerminal() bool

IsTerminal returns true if the Default logger's output is connected to a terminal.

func IsVerbose

func IsVerbose() bool

IsVerbose returns true if verbose/debug mode is enabled on the Default logger. Returns true for both LevelTrace and LevelDebug.

func PathLink(path string, line int) string

PathLink creates a clickable terminal hyperlink for a file path. The line parameter is optional - pass 0 to omit line numbers.

func Pulse added in v0.4.2

func Pulse(msg string, opts ...pulse.Option) *fx.Builder

Pulse creates a new fx.Builder using the Default logger with an animated color pulse on the message text. All characters fade uniformly between colors in the gradient. With no options, the default pulse gradient and speed are used. Use pulse.WithGradient and pulse.WithSpeed to customise the animation.

func RegisterLevel added in v0.5.8

func RegisterLevel(lvl Level, cfg LevelConfig)

RegisterLevel registers a custom log level with the given numeric value and configuration. The level value must not conflict with a built-in level.

After registration the level works with ParseLevel, [Level.MarshalText], [Level.String], and the Default logger's labels, symbols, and styles.

RegisterLevel panics if cfg.Name is empty or level conflicts with a built-in level.

func SetAnimationInterval added in v0.5.0

func SetAnimationInterval(d time.Duration)

SetAnimationInterval sets the minimum animation refresh interval on the Default logger.

func SetColorMode

func SetColorMode(mode ColorMode)

SetColorMode sets the color mode on the Default logger by recreating its Output with the given mode.

func SetElapsedFormatFunc added in v0.4.15

func SetElapsedFormatFunc(fn func(time.Duration) string)

SetElapsedFormatFunc sets the elapsed format function for all loggers (process-global). Delegates to elapsed.SetFormatFunc.

func SetElapsedGradientMax added in v0.6.0

func SetElapsedGradientMax(d time.Duration)

SetElapsedGradientMax sets the max duration for elapsed gradient coloring (process-global). The gradient maps 0 → max onto the configured color stops; 0 disables the gradient. Delegates to elapsed.SetGradientMax.

func SetElapsedMinimum added in v0.4.15

func SetElapsedMinimum(d time.Duration)

SetElapsedMinimum sets the elapsed minimum threshold for all loggers (process-global). Delegates to elapsed.SetMinimum.

func SetElapsedPrecision added in v0.4.15

func SetElapsedPrecision(precision int)

SetElapsedPrecision sets the elapsed precision for all loggers (process-global). Delegates to elapsed.SetPrecision.

func SetElapsedRound added in v0.4.15

func SetElapsedRound(d time.Duration)

SetElapsedRound sets the elapsed rounding granularity for all loggers (process-global). Delegates to elapsed.SetRound.

func SetEnvPrefix added in v0.3.0

func SetEnvPrefix(prefix string)

SetEnvPrefix sets a custom environment variable prefix. Env vars are checked with the custom prefix first, then "CLOG" as fallback.

clog.SetEnvPrefix("MYAPP")
// Now checks MYAPP_LOG_LEVEL, then CLOG_LOG_LEVEL
// Now checks MYAPP_HYPERLINK_PATH_FORMAT, then CLOG_HYPERLINK_PATH_FORMAT
// etc.

func SetExitFunc

func SetExitFunc(fn func(int))

SetExitFunc sets the fatal-exit function on the Default logger.

func SetFieldSort added in v0.4.15

func SetFieldSort(sort Sort)

SetFieldSort sets the field sort order on the Default logger.

func SetFieldStyleLevel added in v0.1.2

func SetFieldStyleLevel(level Level)

SetFieldStyleLevel sets the minimum level for styled fields on the Default logger.

func SetFieldTimeFormat added in v0.1.1

func SetFieldTimeFormat(format string)

SetFieldTimeFormat sets the time format for time fields on the Default logger.

func SetHandler

func SetHandler(h Handler)

SetHandler sets the log handler on the Default logger.

func SetHyperlinkColumnFormat

func SetHyperlinkColumnFormat(format string)

SetHyperlinkColumnFormat configures the URL format for file+line+column hyperlinks (process-global). Accepts a full format string or a preset name (e.g. "vscode"). Delegates to hyperlink.SetColumnFormat.

func SetHyperlinkDirFormat

func SetHyperlinkDirFormat(format string)

SetHyperlinkDirFormat configures the URL format for directory hyperlinks (process-global). Delegates to hyperlink.SetDirFormat.

func SetHyperlinkEnabled added in v0.6.1

func SetHyperlinkEnabled(enabled bool)

SetHyperlinkEnabled enables or disables all hyperlink rendering (process-global). Delegates to hyperlink.SetEnabled.

func SetHyperlinkFileFormat

func SetHyperlinkFileFormat(format string)

SetHyperlinkFileFormat configures the URL format for file-only hyperlinks (process-global). Delegates to hyperlink.SetFileFormat.

func SetHyperlinkLineFormat

func SetHyperlinkLineFormat(format string)

SetHyperlinkLineFormat configures the URL format for file+line hyperlinks (process-global). Accepts a full format string or a preset name (e.g. "vscode"). Delegates to hyperlink.SetLineFormat.

func SetHyperlinkPathFormat

func SetHyperlinkPathFormat(format string)

SetHyperlinkPathFormat configures the generic fallback URL format for any path (process-global). Accepts a full format string or a preset name. Delegates to hyperlink.SetPathFormat.

func SetHyperlinkPreset added in v0.4.5

func SetHyperlinkPreset(name string) error

SetHyperlinkPreset configures all hyperlink format slots using a named preset (process-global). Known presets: cursor, kitty, macvim, subl, textmate, vscode, vscode-insiders, vscodium. Delegates to hyperlink.SetPreset.

func SetIndent added in v0.5.4

func SetIndent(levels int)

SetIndent sets the indent depth on the Default logger.

func SetIndentPrefixSeparator added in v0.5.4

func SetIndentPrefixSeparator(sep string)

SetIndentPrefixSeparator sets the indent prefix separator on the Default logger.

func SetIndentPrefixes added in v0.5.4

func SetIndentPrefixes(prefixes []string)

SetIndentPrefixes sets per-depth indent prefixes on the Default logger.

func SetIndentWidth added in v0.5.4

func SetIndentWidth(width int)

SetIndentWidth sets the indent width on the Default logger.

func SetLabels added in v0.6.0

func SetLabels(labels LabelMap)

SetLabels sets the level labels on the Default logger.

func SetLevel

func SetLevel(level Level)

SetLevel sets the minimum log level on the Default logger.

func SetLevelAlign

func SetLevelAlign(align Align)

SetLevelAlign sets the level-label alignment on the Default logger.

func SetOmitEmpty

func SetOmitEmpty(omit bool)

SetOmitEmpty enables or disables omitting empty fields on the Default logger.

func SetOmitZero

func SetOmitZero(omit bool)

SetOmitZero enables or disables omitting zero-value fields on the Default logger.

func SetOutput

func SetOutput(out *Output)

SetOutput sets the output on the Default logger.

func SetOutputWriter added in v0.3.1

func SetOutputWriter(w io.Writer)

SetOutputWriter sets the output writer on the Default logger with ColorAuto.

func SetParts

func SetParts(order ...Part)

SetParts sets the log-line part order on the Default logger.

func SetPercentFormatFunc added in v0.4.15

func SetPercentFormatFunc(fn func(float64) string)

SetPercentFormatFunc sets the percent format function for all loggers (process-global). Delegates to percent.SetFormatFunc.

func SetPercentPrecision added in v0.4.15

func SetPercentPrecision(precision int)

SetPercentPrecision sets the percent precision for all loggers (process-global). Delegates to percent.SetPrecision.

func SetPercentReverseGradient added in v0.5.11

func SetPercentReverseGradient(reverse bool)

SetPercentReverseGradient sets the percent gradient inversion for all loggers (process-global). Delegates to percent.SetReverseGradient.

func SetPercentScale added in v0.6.0

func SetPercentScale(s float64)

SetPercentScale sets the percent scale for all loggers (process-global). The default is 1.0, meaning percent values are passed as fractions (e.g. 0.75 → "75%"). Set to 100 for legacy 0–100 input. Delegates to percent.SetScale.

func SetQuantityUnitsIgnoreCase added in v0.4.15

func SetQuantityUnitsIgnoreCase(ignoreCase bool)

SetQuantityUnitsIgnoreCase sets case-insensitive quantity unit matching for all loggers (process-global). Delegates to quantity.SetUnitsIgnoreCase.

func SetQuoteChar

func SetQuoteChar(char rune)

SetQuoteChar sets the quote character on the Default logger.

func SetQuoteChars

func SetQuoteChars(openChar, closeChar rune)

SetQuoteChars sets the opening and closing quote characters on the Default logger.

func SetQuoteMode

func SetQuoteMode(mode QuoteMode)

SetQuoteMode sets the quoting behaviour on the Default logger.

func SetReportTimestamp

func SetReportTimestamp(report bool)

SetReportTimestamp enables or disables timestamps on the Default logger.

func SetSeparatorText added in v0.4.15

func SetSeparatorText(sep string)

SetSeparatorText sets the key/value separator on the Default logger.

func SetStyles

func SetStyles(styles *style.Config)

SetStyles sets the display styles on the Default logger.

func SetSymbols added in v0.6.0

func SetSymbols(symbols LabelMap)

SetSymbols sets the level symbols on the Default logger.

func SetTimeFormat

func SetTimeFormat(format string)

SetTimeFormat sets the timestamp format on the Default logger.

func SetTimeLocation

func SetTimeLocation(loc *time.Location)

SetTimeLocation sets the timestamp timezone on the Default logger.

func SetTreeChars added in v0.5.6

func SetTreeChars(chars TreeChars)

SetTreeChars sets the tree-drawing characters on the Default logger.

func SetVerbose added in v0.3.0

func SetVerbose(verbose bool)

SetVerbose enables or disables verbose mode on the Default logger. When verbose is true, it always enables debug logging. When false, it respects the log level environment variable if set.

func Shimmer added in v0.4.2

func Shimmer(msg string, opts ...shimmer.Option) *fx.Builder

Shimmer creates a new fx.Builder using the Default logger with an animated gradient shimmer on the message text. Each character is colored independently based on its position in the wave. With no options, the default shimmer gradient, direction, and speed are used. Use shimmer.WithGradient, shimmer.WithDirection, and shimmer.WithSpeed to customise the animation.

func Spinner

func Spinner(msg string, opts ...spinner.Option) *fx.Builder

Spinner creates a new fx.Builder using the Default logger with a rotating spinner animation.

func WithContext added in v0.5.0

func WithContext(ctx context.Context) context.Context

WithContext stores the Default logger in ctx.

Types

type Align added in v0.4.17

type Align int

Align controls how text is aligned within a fixed-width column.

const (
	// AlignNone disables alignment padding.
	AlignNone Align = iota
	// AlignLeft left-aligns text (pads with trailing spaces).
	AlignLeft
	// AlignRight right-aligns text (pads with leading spaces).
	AlignRight
	// AlignCenter center-aligns text (pads with leading and trailing spaces).
	AlignCenter
)

type ColorMode

type ColorMode int

ColorMode controls how a Logger determines color and hyperlink output.

ColorMode implements encoding.TextMarshaler and encoding.TextUnmarshaler, so it works directly with flag.TextVar and most flag libraries.

const (
	// ColorAuto uses global detection (terminal, NO_COLOR, etc.). This is the default.
	ColorAuto ColorMode = iota // auto
	// ColorAlways forces colors and hyperlinks, even when output is not a TTY.
	ColorAlways // always
	// ColorNever disables colors and hyperlinks.
	ColorNever // never
)

func (ColorMode) MarshalText added in v0.3.0

func (m ColorMode) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (ColorMode) String added in v0.3.0

func (i ColorMode) String() string

func (*ColorMode) UnmarshalText added in v0.3.0

func (m *ColorMode) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

type Config

type Config struct {
	// Output is the output to use (defaults to [Stdout]([ColorAuto])).
	Output *Output
	// Styles allows customising the visual styles.
	Styles *style.Config
	// Verbose enables debug level logging and timestamps.
	Verbose bool
}

Config holds configuration options for the Default logger.

type Context

type Context struct {
	// contains filtered or unexported fields
}

Context builds a sub-logger with preset fields. Created by Logger.With. Finalise with Context.Logger.

func With

func With() *Context

With returns a Context for building a sub-logger from the Default logger.

func (*Context) Column

func (c *Context) Column(key, path string, line, column int) *Context

Column adds a file path field with a line and column number as a clickable terminal hyperlink. Respects the logger's ColorMode setting.

func (*Context) Dedent added in v0.5.5

func (c *Context) Dedent() *Context

Dedent removes one indent level from the sub-logger, down to a minimum of zero. Useful for pulling a child logger back toward the root when a parent logger is already indented.

func (*Context) Depth added in v0.5.4

func (c *Context) Depth(n int) *Context

Depth adds multiple indent levels at once. Equivalent to calling Context.Indent n times.

func (*Context) Dict

func (c *Context) Dict(key string, dict *Event) *Context

Dict adds a group of fields under a key prefix using dot notation. Build the nested fields using Dict to create a field-only Event:

logger := clog.With().Dict("db", clog.Dict().
    Str("host", "localhost").
    Int("port", 5432),
).Logger()

func (*Context) Indent added in v0.5.4

func (c *Context) Indent() *Context

Indent adds one indent level to the sub-logger. Chainable: With().Indent().Indent().Logger() produces two levels of indentation.

func (*Context) Line

func (c *Context) Line(key, path string, line int) *Context

Line adds a file path field with a line number as a clickable terminal hyperlink. Respects the logger's ColorMode setting.

func (c *Context) Link(key, url, text string) *Context

Link adds a field as a clickable terminal hyperlink with custom URL and display text. Respects the logger's ColorMode setting.

func (*Context) Logger

func (c *Context) Logger() *Logger

Logger returns a new Logger with the accumulated fields and symbol. The returned Logger shares the parent's mutex to prevent interleaved output.

func (*Context) Path

func (c *Context) Path(key, path string) *Context

Path adds a file path field as a clickable terminal hyperlink. Respects the logger's ColorMode setting.

func (*Context) Symbol added in v0.6.0

func (c *Context) Symbol(symbol string) *Context

Symbol sets a custom symbol for the sub-logger.

func (*Context) Tree added in v0.5.6

func (c *Context) Tree(pos TreePos) *Context

Tree adds one tree-nesting level with the given position. Each call deepens the tree by one level, drawing box-drawing connectors (├──, └──, │) automatically. Combine with Context.Indent to add space-based indent before the tree connectors.

func (*Context) URL added in v0.2.1

func (c *Context) URL(key, url string) *Context

URL adds a field as a clickable terminal hyperlink where the URL is also the display text. Respects the logger's ColorMode setting.

type DividerBuilder added in v0.5.1

type DividerBuilder struct {
	// contains filtered or unexported fields
}

DividerBuilder configures and renders a horizontal divider line. Create one with Logger.Divider or the package-level Divider function.

Call DividerBuilder.Send to render a plain line, or DividerBuilder.Msg to render a line with an embedded title:

clog.Divider().Send()
clog.Divider().Msg("Build Phase")
clog.Divider().Char('═').Align(AlignCenter).Msg("Results")

func Divider added in v0.5.1

func Divider() *DividerBuilder

Divider returns a new DividerBuilder for rendering a horizontal rule using the Default logger.

func (*DividerBuilder) Align added in v0.5.1

func (b *DividerBuilder) Align(align Align) *DividerBuilder

Align sets the title alignment within the divider line. Default is AlignLeft.

func (*DividerBuilder) Char added in v0.5.1

func (b *DividerBuilder) Char(char rune) *DividerBuilder

Char sets the character used for the divider line. Default is '─'.

func (*DividerBuilder) Msg added in v0.5.1

func (b *DividerBuilder) Msg(title string)

Msg renders the divider with the given title text.

func (*DividerBuilder) Send added in v0.5.1

func (b *DividerBuilder) Send()

Send renders a plain divider line without a title.

func (*DividerBuilder) Width added in v0.5.1

func (b *DividerBuilder) Width(width int) *DividerBuilder

Width overrides the divider width in columns. By default the terminal width is used, falling back to 80 for non-TTY output.

type Entry

type Entry struct {
	Time    time.Time `json:"time,omitzero"`
	Level   Level     `json:"level"`
	Symbol  string    `json:"symbol,omitempty"`
	Indent  int       `json:"indent,omitempty"`
	Message string    `json:"message"`
	Fields  []Field   `json:"fields,omitempty"`
	Tree    []TreePos `json:"tree,omitempty"`
}

Entry represents a completed log entry passed to a Handler.

type Event

type Event struct {
	// contains filtered or unexported fields
}

Event represents a log event being constructed. All methods are safe to call on a nil receiver - disabled events (when the log level is below the logger's minimum) are no-ops.

func Debug

func Debug() *Event

Debug returns a new debug-level Event from the Default logger.

func Dict

func Dict() *Event

Dict returns a new detached Event for use as a nested dictionary field. The event uses the Default logger's output for hyperlink/color resolution.

func Dry

func Dry() *Event

Dry returns a new dry-level Event from the Default logger.

func Error

func Error() *Event

Error returns a new error-level Event from the Default logger.

func Fatal

func Fatal() *Event

Fatal returns a new fatal-level Event from the Default logger.

func Info

func Info() *Event

Info returns a new info-level Event from the Default logger.

func Log added in v0.5.8

func Log(level Level) *Event

Log returns a new Event at the given level from the Default logger. Use this for custom levels registered with RegisterLevel.

func Trace

func Trace() *Event

Trace returns a new trace-level Event from the Default logger.

func Warn

func Warn() *Event

Warn returns a new warn-level Event from the Default logger.

func (*Event) Any

func (e *Event) Any(key string, val any) *Event

Any adds a field with an arbitrary value.

func (*Event) Anys

func (e *Event) Anys(key string, vals []any) *Event

Anys adds a slice of arbitrary values. Individual elements are highlighted using reflection to determine their type.

func (*Event) Base64 added in v0.4.8

func (e *Event) Base64(key string, val []byte) *Event

Base64 adds a []byte field encoded as a base64 string.

func (*Event) Bool

func (e *Event) Bool(key string, val bool) *Event

Bool adds a bool field.

func (*Event) Bools

func (e *Event) Bools(key string, vals []bool) *Event

Bools adds a bool slice field.

func (*Event) Bytes added in v0.4.7

func (e *Event) Bytes(key string, val []byte) *Event

Bytes adds a []byte field. If val is valid JSON it is stored as [RawJSON] with syntax highlighting; otherwise it is stored as a plain string.

func (*Event) Column

func (e *Event) Column(key, path string, line, column int) *Event

Column adds a file path field with a line and column number as a clickable terminal hyperlink. Respects the logger's ColorMode setting.

func (*Event) Dict

func (e *Event) Dict(key string, dict *Event) *Event

Dict adds a group of fields under a key prefix using dot notation. Build the nested fields using Dict to create a field-only Event:

clog.Info().Dict("request", clog.Dict().
    Str("method", "GET").
    Int("status", 200),
).Msg("handled")
// Output: INF ℹ️ handled request.method=GET request.status=200

func (*Event) Duration added in v0.2.2

func (e *Event) Duration(key string, val time.Duration) *Event

Duration adds a time.Duration field.

func (*Event) Durations added in v0.2.2

func (e *Event) Durations(key string, vals []time.Duration) *Event

Durations adds a time.Duration slice field.

func (*Event) Elapsed added in v0.5.1

func (e *Event) Elapsed(key string) *Event

Elapsed adds an elapsed-time field at the current position in the field list. The duration is measured from the first Elapsed call on this event until the event is finalised with Event.Send, Event.Msg, or Event.Msgf.

The key parameter is the field name (e.g. "elapsed"). The field uses the same formatting and styling as fx.Builder.Elapsed.

e := clog.Info().Str("step", "migrate").Elapsed("elapsed")
runMigrations()
e.Msg("done")
// Output: INF ℹ️ done step=migrate elapsed=2s

func (*Event) Err

func (e *Event) Err(err error) *Event

Err attaches an error to the event. No-op if err is nil.

If the event is finalised with Event.Send, the error message becomes the log message with no extra fields. If finalised with Event.Msg or Event.Msgf, the error is added as an "error" field alongside the message.

func (*Event) Errs added in v0.4.10

func (e *Event) Errs(key string, vals []error) *Event

Errs adds an error slice field. Each error is converted to its message string; nil errors are rendered as Nil ("<nil>").

func (*Event) Float64

func (e *Event) Float64(key string, val float64) *Event

Float64 adds a float64 field.

func (*Event) Floats64

func (e *Event) Floats64(key string, vals []float64) *Event

Floats64 adds a float64 slice field.

func (*Event) Func added in v0.4.10

func (e *Event) Func(fn func(*Event)) *Event

Func executes fn with the event if the event is enabled (non-nil). This is useful for computing expensive fields lazily - the callback is skipped entirely when the log level is disabled.

func (*Event) Hex added in v0.4.8

func (e *Event) Hex(key string, val []byte) *Event

Hex adds a []byte field encoded as a hex string.

func (*Event) Int

func (e *Event) Int(key string, val int) *Event

Int adds an int field.

func (*Event) Int64 added in v0.4.1

func (e *Event) Int64(key string, val int64) *Event

Int64 adds an int64 field.

func (*Event) Ints

func (e *Event) Ints(key string, vals []int) *Event

Ints adds an int slice field.

func (*Event) Ints64 added in v0.4.4

func (e *Event) Ints64(key string, vals []int64) *Event

Ints64 adds an int64 slice field.

func (*Event) JSON added in v0.4.4

func (e *Event) JSON(key string, val any) *Event

JSON marshals val to JSON and adds it as a highlighted field. On marshal error the field value is the error string.

func (*Event) Line

func (e *Event) Line(key, path string, line int) *Event

Line adds a file path field with a line number as a clickable terminal hyperlink. Respects the logger's ColorMode setting.

func (e *Event) Link(key, url, text string) *Event

Link adds a field as a clickable terminal hyperlink with custom URL and display text. Respects the logger's ColorMode setting.

func (*Event) Msg

func (e *Event) Msg(msg string)

Msg finalises the event and writes the log entry. If Event.Err was called, the error is included as an "error" field. For LevelFatal events, Msg calls os.Exit(1) after writing.

func (*Event) Msgf

func (e *Event) Msgf(format string, args ...any)

Msgf finalises the event with a formatted message.

func (*Event) Parts added in v0.5.1

func (e *Event) Parts(parts ...Part) *Event

Parts overrides the log-line part order for this entry. Parts not included are hidden. This does not affect the logger's global parts.

func (*Event) Path

func (e *Event) Path(key, path string) *Event

Path adds a file path field as a clickable terminal hyperlink. Respects the logger's ColorMode setting.

func (*Event) Percent added in v0.3.0

func (e *Event) Percent(key string, val float64, opts ...percent.Option) *Event

Percent adds a percentage field with gradient color styling. Values are clamped to [0, Scale] (default scale is 1, so 0.75 → "75%"). The color is interpolated from the style.Config.PercentGradient stops (default: red → yellow → green).

Use percent.WithReverseGradient to flip the gradient for this field:

e.Percent("cpu", usage, percent.WithReverseGradient())

func (*Event) Quantities added in v0.2.2

func (e *Event) Quantities(key string, vals []string) *Event

Quantities adds a quantity string slice field. Each element is styled with style.Config.FieldQuantityNumber and style.Config.FieldQuantityUnit.

func (*Event) Quantity added in v0.2.2

func (e *Event) Quantity(key, val string) *Event

Quantity adds a quantity string field where numeric and unit segments are styled independently (e.g. "5m", "5.1km", "100MB"). The value is styled with style.Config.FieldQuantityNumber and style.Config.FieldQuantityUnit.

func (*Event) RawJSON added in v0.4.4

func (e *Event) RawJSON(key string, val []byte) *Event

RawJSON adds a field with pre-serialized JSON bytes, emitted verbatim without quoting or escaping. The bytes must be valid JSON.

func (*Event) Send

func (e *Event) Send()

Send finalises the event. If Event.Err was called, the error message is used as the log message (no "error" field is added). Any other fields on the event are preserved. If Event.Err was not called, the message is empty.

func (*Event) Str

func (e *Event) Str(key, val string) *Event

Str adds a string field.

func (*Event) Stringer

func (e *Event) Stringer(key string, val fmt.Stringer) *Event

Stringer adds a field by calling the value's String method. No-op if val is nil.

func (*Event) Stringers

func (e *Event) Stringers(key string, vals []fmt.Stringer) *Event

Stringers adds a field with a slice of fmt.Stringer values.

func (*Event) Strs

func (e *Event) Strs(key string, vals []string) *Event

Strs adds a string slice field.

func (*Event) Symbol added in v0.6.0

func (e *Event) Symbol(symbol string) *Event

Symbol overrides the default emoji symbol for this entry.

func (*Event) Time added in v0.1.1

func (e *Event) Time(key string, val time.Time) *Event

Time adds a time.Time field.

func (*Event) Times added in v0.4.4

func (e *Event) Times(key string, vals []time.Time) *Event

Times adds a time.Time slice field.

func (*Event) URL added in v0.2.1

func (e *Event) URL(key, url string) *Event

URL adds a field as a clickable terminal hyperlink where the URL is also the display text. Respects the logger's ColorMode setting.

func (*Event) Uint added in v0.4.1

func (e *Event) Uint(key string, val uint) *Event

Uint adds a uint field.

func (*Event) Uint64

func (e *Event) Uint64(key string, val uint64) *Event

Uint64 adds a uint64 field.

func (*Event) Uints added in v0.4.4

func (e *Event) Uints(key string, vals []uint) *Event

Uints adds a uint slice field.

func (*Event) Uints64

func (e *Event) Uints64(key string, vals []uint64) *Event

Uints64 adds a uint64 slice field.

func (*Event) When added in v0.5.1

func (e *Event) When(condition bool, fn func(*Event)) *Event

When calls fn with the event if condition is true and the event is enabled (non-nil). This is useful for conditionally adding fields without breaking the chain.

type Field

type Field = core.Field

Field is a typed key-value pair attached to a log entry.

type Handler

type Handler interface {
	Log(Entry)
}

Handler processes log entries. Implement this interface to customise how log entries are formatted and output (e.g. JSON logging).

When a Handler is set on a Logger, the Logger handles level checking, field accumulation, timestamps, and mutex locking. The Handler only needs to format and write the entry.

type HandlerFunc

type HandlerFunc func(Entry)

HandlerFunc is an adapter to use ordinary functions as Handler values.

func (HandlerFunc) Log

func (f HandlerFunc) Log(e Entry)

Log calls f(e).

type LabelMap added in v0.6.0

type LabelMap map[Level]string

LabelMap maps levels to strings (used for labels, symbols, etc.).

func DefaultLabels

func DefaultLabels() LabelMap

DefaultLabels returns a copy of the default level labels.

func DefaultSymbols added in v0.6.0

func DefaultSymbols() LabelMap

DefaultSymbols returns a copy of the default emoji symbols for each level.

type Level

type Level = level.Level

Level represents a log level.

Level implements encoding.TextMarshaler and encoding.TextUnmarshaler, so it works directly with flag.TextVar and most flag libraries.

func GetLevel

func GetLevel() Level

GetLevel returns the current log level of the Default logger.

func Levels added in v0.5.8

func Levels() []Level

Levels returns all registered levels (built-in and custom) in ascending severity order.

func ParseLevel added in v0.4.17

func ParseLevel(s string) (Level, error)

ParseLevel maps a level name string to a Level value. It accepts the canonical names ("trace", "debug", "info", "dry", "warn", "error", "fatal") plus aliases ("warning" → Warn, "critical" → Fatal). Matching is case-insensitive.

type LevelConfig added in v0.5.8

type LevelConfig struct {
	Label  string // short display label (e.g. "SCS") [default: uppercase Name, max 3 chars]
	Name   string // canonical name for ParseLevel/MarshalText (e.g. "success") [required]
	Style  Style  // lipgloss style for the level label [default: nil]
	Symbol string // emoji symbol (e.g. "✅") [default: ""]
}

LevelConfig configures a custom log level for use with RegisterLevel.

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

Logger is the main structured logger.

func Ctx added in v0.5.0

func Ctx(ctx context.Context) *Logger

Ctx retrieves the logger from ctx. Returns Default if ctx is nil or contains no logger.

func New

func New(output *Output) *Logger

New creates a new Logger that writes to the given Output. If output is nil, it defaults to Stdout with ColorAuto.

func NewWriter added in v0.3.1

func NewWriter(w io.Writer) *Logger

NewWriter creates a new Logger that writes to w with ColorAuto.

func (*Logger) Bar added in v0.5.0

func (l *Logger) Bar(msg string, total int, opts ...bar.Option) *fx.Builder

Bar creates a new fx.Builder with a determinate progress bar animation. total is the maximum progress value. Use [Update.SetProgress] to update progress.

func (*Logger) Debug

func (l *Logger) Debug() *Event

Debug returns a new Event at debug level, or nil if debug is disabled.

func (*Logger) Dict added in v0.6.0

func (l *Logger) Dict() *Event

Dict returns a new detached Event for use as a nested dictionary field. The event uses the logger's output for hyperlink/color resolution.

func (*Logger) Divider added in v0.5.1

func (l *Logger) Divider() *DividerBuilder

Divider returns a new DividerBuilder for rendering a horizontal rule.

func (*Logger) Dry

func (l *Logger) Dry() *Event

Dry returns a new Event at dry level, or nil if dry is disabled.

func (*Logger) Error

func (l *Logger) Error() *Event

Error returns a new Event at error level, or nil if error is disabled.

func (*Logger) Fatal

func (l *Logger) Fatal() *Event

Fatal returns a new Event at fatal level.

func (*Logger) Group added in v0.5.0

func (l *Logger) Group(ctx context.Context) *fx.Group

Group creates a new animation group.

func (*Logger) Info

func (l *Logger) Info() *Event

Info returns a new Event at info level, or nil if info is disabled.

func (*Logger) Level added in v0.4.17

func (l *Logger) Level() Level

Level returns the current minimum log level.

func (*Logger) LevelEnabled added in v0.6.0

func (l *Logger) LevelEnabled(level Level) bool

LevelEnabled reports whether the logger handles records at the given level.

func (*Logger) Log added in v0.5.8

func (l *Logger) Log(level Level) *Event

Log returns a new Event at the given level. Use this for custom levels registered with RegisterLevel.

func (*Logger) LogFields added in v0.6.0

func (l *Logger) LogFields(level Level, ts time.Time, msg string, fields []Field)

LogFields logs a message at the given level with the provided timestamp and fields. This is used by adapters (e.g. [sloghandler]) that build fields externally. Unlike direct Fatal() calls, LogFields does not trigger os.Exit for LevelFatal events -- adapters should not cause process termination.

func (*Logger) Output added in v0.3.1

func (l *Logger) Output() *Output

Output returns the logger's Output.

func (*Logger) Pulse added in v0.4.17

func (l *Logger) Pulse(msg string, opts ...pulse.Option) *fx.Builder

Pulse creates a new fx.Builder with an animated color pulse on the message text. All characters fade uniformly between colors in the gradient. With no options, the default pulse gradient and speed are used. Use pulse.WithGradient and pulse.WithSpeed to customise the animation.

func (*Logger) SetAnimationInterval added in v0.5.0

func (l *Logger) SetAnimationInterval(d time.Duration)

SetAnimationInterval sets a minimum refresh interval for all animations (spinners, bars, pulse, shimmer). Any animation whose built-in tick rate is faster than d will be clamped to d. The default is 67ms (~15fps). Zero means use built-in rates unchanged.

func (*Logger) SetColorMode

func (l *Logger) SetColorMode(mode ColorMode)

SetColorMode sets the color mode by recreating the logger's Output with the given mode.

func (*Logger) SetExitFunc

func (l *Logger) SetExitFunc(fn func(int))

SetExitFunc sets the function called by Fatal-level events. Defaults to os.Exit. This can be used in tests to intercept fatal exits. If fn is nil, the default os.Exit is used.

func (*Logger) SetFieldSort added in v0.4.15

func (l *Logger) SetFieldSort(sort Sort)

SetFieldSort sets the sort order for fields in log output. Default SortNone preserves insertion order.

func (*Logger) SetFieldStyleLevel added in v0.1.2

func (l *Logger) SetFieldStyleLevel(level Level)

SetFieldStyleLevel sets the minimum log level at which field values are styled (colored). Events below this level render fields as plain text. Defaults to LevelInfo.

func (*Logger) SetFieldTimeFormat added in v0.1.1

func (l *Logger) SetFieldTimeFormat(format string)

SetFieldTimeFormat sets the format string used for time.Time field values added via Event.Time and [Context.Time]. Defaults to time.RFC3339.

func (*Logger) SetHandler

func (l *Logger) SetHandler(h Handler)

SetHandler sets a custom log handler. When set, the handler receives all log entries instead of the built-in pretty formatter.

func (*Logger) SetIndent added in v0.5.4

func (l *Logger) SetIndent(levels int)

SetIndent sets the indent depth (number of indent levels) on the logger.

func (*Logger) SetIndentPrefixSeparator added in v0.5.4

func (l *Logger) SetIndentPrefixSeparator(sep string)

SetIndentPrefixSeparator sets the separator appended after an indent prefix. Defaults to " " (a single space).

func (*Logger) SetIndentPrefixes added in v0.5.4

func (l *Logger) SetIndentPrefixes(prefixes []string)

SetIndentPrefixes sets per-depth decorations placed after the space-based indent. A trailing space is appended automatically. Prefixes are cycled: at depth N the prefix is prefixes[(N-1) % len(prefixes)]. For example, with []string{"│"}, depth 2 produces " │ " (4 spaces + "│" + space). Pass nil to clear.

func (*Logger) SetIndentWidth added in v0.5.4

func (l *Logger) SetIndentWidth(width int)

SetIndentWidth sets the number of spaces per indent level. Defaults to 2.

func (*Logger) SetLabelWidth added in v0.4.17

func (l *Logger) SetLabelWidth(width int)

SetLabelWidth sets an explicit minimum width for level labels. If width is 0, the width is computed automatically from the current labels.

func (*Logger) SetLabels

func (l *Logger) SetLabels(labels LabelMap)

SetLabels sets the level labels used in log output. Pass a map from Level to label string (e.g. {LevelWarn: "WARN"}). Missing levels fall back to the defaults.

func (*Logger) SetLevel

func (l *Logger) SetLevel(level Level)

SetLevel sets the minimum log level.

func (*Logger) SetLevelAlign

func (l *Logger) SetLevelAlign(align Align)

SetLevelAlign sets the alignment mode for level labels.

func (*Logger) SetNonTTYLevel added in v0.5.12

func (l *Logger) SetNonTTYLevel(level Level)

SetNonTTYLevel sets the minimum log level for non-TTY writers (CI, piped output, etc.). Events below this level are suppressed when the logger's output is not connected to a terminal, including animation progress lines. Pass UnsetLevel to restore the default behaviour.

func (*Logger) SetOmitEmpty

func (l *Logger) SetOmitEmpty(omit bool)

SetOmitEmpty enables or disables omitting fields with empty values. Empty means nil, empty strings, and nil or empty slices/maps.

func (*Logger) SetOmitZero

func (l *Logger) SetOmitZero(omit bool)

SetOmitZero enables or disables omitting fields with zero values. Zero means the zero value for any type (0, false, "", nil, etc.). This is a superset of Logger.SetOmitEmpty.

func (*Logger) SetOutput

func (l *Logger) SetOutput(out *Output)

SetOutput sets the output.

func (*Logger) SetOutputWriter added in v0.3.1

func (l *Logger) SetOutputWriter(w io.Writer)

SetOutputWriter sets the output writer with ColorAuto.

func (*Logger) SetParts

func (l *Logger) SetParts(parts ...Part)

SetParts sets the order in which parts appear in log output. Parts not included in the order are hidden. Parts can be reordered freely. Panics if no parts are provided.

func (*Logger) SetQuoteChar

func (l *Logger) SetQuoteChar(char rune)

SetQuoteChar sets the character used to quote field values that contain spaces or special characters. The default (zero value) uses Go-style double-quoted strings via strconv.Quote. Setting a non-zero rune wraps values with that character on both sides (e.g. '\").

For asymmetric quotes (e.g. '[' and ']'), use Logger.SetQuoteChars.

func (*Logger) SetQuoteChars

func (l *Logger) SetQuoteChars(openChar, closeChar rune)

SetQuoteChars sets separate opening and closing characters for quoting field values (e.g. '[' and ']', or '«' and '»').

func (*Logger) SetQuoteMode

func (l *Logger) SetQuoteMode(mode QuoteMode)

SetQuoteMode sets the quoting behaviour for field values. QuoteAuto (default) quotes only when needed; QuoteAlways always quotes string/error/default-kind values; QuoteNever never quotes.

func (*Logger) SetReportTimestamp

func (l *Logger) SetReportTimestamp(report bool)

SetReportTimestamp enables or disables timestamp reporting.

func (*Logger) SetSeparatorText added in v0.4.15

func (l *Logger) SetSeparatorText(sep string)

SetSeparatorText sets the separator between field keys and values. Defaults to "=".

func (*Logger) SetStyles

func (l *Logger) SetStyles(styles *style.Config)

SetStyles sets the display styles. If styles is nil, DefaultStyles is used.

func (*Logger) SetSymbols added in v0.6.0

func (l *Logger) SetSymbols(symbols LabelMap)

SetSymbols sets the emoji symbols used for each level. Pass a map from Level to symbol string. Missing levels fall back to the defaults.

func (*Logger) SetTimeFormat

func (l *Logger) SetTimeFormat(format string)

SetTimeFormat sets the timestamp format string.

func (*Logger) SetTimeLocation

func (l *Logger) SetTimeLocation(loc *time.Location)

SetTimeLocation sets the timezone for timestamps. Defaults to time.Local. If loc is nil, time.Local is used.

func (*Logger) SetTreeChars added in v0.5.6

func (l *Logger) SetTreeChars(chars TreeChars)

SetTreeChars sets the box-drawing characters used for tree indentation. See DefaultTreeChars for the defaults.

func (*Logger) Shimmer added in v0.4.17

func (l *Logger) Shimmer(msg string, opts ...shimmer.Option) *fx.Builder

Shimmer creates a new fx.Builder with an animated gradient shimmer on the message text. Each character is colored independently based on its position in the wave. With no options, the default shimmer gradient, direction, and speed are used. Use shimmer.WithGradient, shimmer.WithDirection, and shimmer.WithSpeed to customise the animation.

func (*Logger) Spinner added in v0.4.17

func (l *Logger) Spinner(msg string, opts ...spinner.Option) *fx.Builder

Spinner creates a new fx.Builder with a rotating spinner animation.

func (*Logger) Trace

func (l *Logger) Trace() *Event

Trace returns a new Event at trace level, or nil if trace is disabled.

func (*Logger) Warn

func (l *Logger) Warn() *Event

Warn returns a new Event at warn level, or nil if warn is disabled.

func (*Logger) With

func (l *Logger) With() *Context

With returns a Context for building a sub-logger with preset fields.

logger := clog.With().Str("component", "auth").Logger()
logger.Info().Str("user", "john").Msg("Authenticated")

func (*Logger) WithContext added in v0.5.0

func (l *Logger) WithContext(ctx context.Context) context.Context

WithContext returns a copy of ctx with the logger stored as a value.

type Output added in v0.3.1

type Output struct {
	// contains filtered or unexported fields
}

Output bundles an io.Writer with its detected terminal capabilities (TTY, width, color profile). Each Logger holds an *Output so that capability detection is per-writer instead of per-process.

func NewOutput added in v0.3.1

func NewOutput(w io.Writer, mode ColorMode) *Output

NewOutput creates a new Output that wraps w. TTY detection is automatic for writers that expose an Fd() uintptr method (e.g. *os.File). The ColorMode determines how colors are handled:

func Stderr added in v0.3.1

func Stderr(mode ColorMode) *Output

Stderr returns a new Output for os.Stderr.

func Stdout added in v0.3.1

func Stdout(mode ColorMode) *Output

Stdout returns a new Output for os.Stdout.

func TestOutput added in v0.3.1

func TestOutput(w io.Writer) *Output

TestOutput returns a non-TTY Output with colors disabled, suitable for tests.

func (*Output) ColorsDisabled added in v0.3.1

func (o *Output) ColorsDisabled() bool

ColorsDisabled returns true if this output should suppress colors.

func (o *Output) Hyperlink(url, text string) string

Hyperlink wraps text in an OSC 8 terminal hyperlink, using the Output's color settings. Satisfies fx.Output.

func (*Output) IsTTY added in v0.3.1

func (o *Output) IsTTY() bool

IsTTY returns true if the writer is connected to a terminal.

func (o *Output) PathLink(path string, line, column int) string

PathLink creates a clickable terminal hyperlink for a file path, using the Output's color settings. Satisfies fx.Output.

func (*Output) RefreshWidth added in v0.3.1

func (o *Output) RefreshWidth()

RefreshWidth clears the cached terminal width so that the next call to Output.Width re-queries the terminal.

func (*Output) Renderer added in v0.3.1

func (o *Output) Renderer() *lipgloss.Renderer

Renderer returns the lipgloss.Renderer configured for this output.

func (*Output) Width added in v0.3.1

func (o *Output) Width() int

Width returns the terminal width, or 0 for non-TTY writers. The value is lazily detected and cached; call Output.RefreshWidth to re-detect.

func (*Output) Writer added in v0.3.1

func (o *Output) Writer() io.Writer

Writer returns the underlying io.Writer.

type Part

type Part = core.Part

Part identifies a component of a formatted log line.

func DefaultParts

func DefaultParts() []Part

DefaultParts returns the default ordering of log line parts: timestamp, level, symbol, message, fields.

type QuoteMode

type QuoteMode = core.QuoteMode

QuoteMode controls how field values are quoted in log output.

type Sort added in v0.4.14

type Sort = core.Sort

Sort controls how fields are sorted in output.

type Style added in v0.4.4

type Style = *lipgloss.Style

Style is a pointer type (*lipgloss.Style). A nil Style means "no style" and is safe to pass wherever a Style is accepted.

type TaskResult added in v0.5.4

type TaskResult = fx.TaskResult

Convenience aliases so callers can reference common fx types without importing subpackages directly.

type TreeChars added in v0.5.6

type TreeChars = core.TreeChars

TreeChars defines the box-drawing characters used by tree indentation. Override with Logger.SetTreeChars.

func DefaultTreeChars added in v0.5.6

func DefaultTreeChars() TreeChars

DefaultTreeChars returns the default box-drawing characters for tree indentation.

type TreePos added in v0.5.6

type TreePos = core.TreePos

TreePos identifies a node's position among its siblings in a tree.

type Update added in v0.6.0

type Update = fx.Update

Convenience aliases so callers can reference common fx types without importing subpackages directly.

Directories

Path Synopsis
banner command
bar command
group command
indent command
json command
levels command
pulse command
shimmer command
spinner command
styles command
tree command
field
elapsed
Package elapsed provides elapsed time field configuration for clog.
Package elapsed provides elapsed time field configuration for clog.
hyperlink
Package hyperlink provides terminal hyperlink configuration for clog.
Package hyperlink provides terminal hyperlink configuration for clog.
json
Package json provides JSON field styling and rendering for clog.
Package json provides JSON field styling and rendering for clog.
percent
Package percent provides options for percent field rendering in clog.
Package percent provides options for percent field rendering in clog.
quantity
Package quantity provides quantity field configuration for clog.
Package quantity provides quantity field configuration for clog.
fx
Package fx provides terminal animation types (spinners, progress bars, shimmers, and pulses) with composable builders and group orchestration.
Package fx provides terminal animation types (spinners, progress bars, shimmers, and pulses) with composable builders and group orchestration.
bar
Package bar provides bar animation styles and presets for clog.
Package bar provides bar animation styles and presets for clog.
bar/widget
Package widget provides composable text widgets for progress bars.
Package widget provides composable text widgets for progress bars.
pulse
Package pulse provides pulse animation rendering for clog.
Package pulse provides pulse animation rendering for clog.
shimmer
Package shimmer provides shimmer animation rendering for clog.
Package shimmer provides shimmer animation rendering for clog.
spinner
Package spinner provides spinner animation styles and presets for clog.
Package spinner provides spinner animation styles and presets for clog.
internal
core
Package core provides shared value types used by both the root clog package and subpackages like fx/.
Package core provides shared value types used by both the root clog package and subpackages like fx/.
gradient
Package gradient provides color gradient interpolation shared by the style and fx packages.
Package gradient provides color gradient interpolation shared by the style and fx packages.
numfmt
Package numfmt provides human-readable formatting for byte counts.
Package numfmt provides human-readable formatting for byte counts.
Package level defines the Level type and built-in level constants for clog.
Package level defines the Level type and built-in level constants for clog.
Package sloghandler provides a slog.Handler adapter for clog.
Package sloghandler provides a slog.Handler adapter for clog.
Package style provides styling types, color stops, and defaults for clog.
Package style provides styling types, color stops, and defaults for clog.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL