bullets

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2025 License: MIT Imports: 8 Imported by: 0

README

Bullets

Go Report Card GitHub release linter coverage tests vulnerability-scan Release Build License

A colorful terminal logger for Go with bullet-style output, inspired by goreleaser's beautiful CLI output.

Features

  • 🎨 Colorful terminal output with ANSI colors
  • 🔘 Configurable bullet symbols (default circles, optional special symbols, custom icons)
  • 📊 Support for log levels (Debug, Info, Warn, Error, Fatal)
  • 📝 Structured logging with fields
  • ⏱️ Timing information for long-running operations
  • 🔄 Indentation/padding support for nested operations
  • ⏳ Animated spinners with multiple styles (Braille, Circle, Bounce)
  • 🔄 Updatable bullets - Update previously rendered bullets in real-time
  • 📊 Progress indicators - Show progress bars within bullets
  • 🎯 Batch operations - Update multiple bullets simultaneously
  • 🧵 Thread-safe operations
  • 🚀 Zero external dependencies (stdlib only)

Demo

demo

Installation

go get github.com/sgaunet/bullets

Quick Start

package main

import (
    "os"
    "github.com/sgaunet/bullets"
)

func main() {
    logger := bullets.New(os.Stdout)

    logger.Info("building")
    logger.IncreasePadding()
    logger.Info("binary=dist/app_linux_amd64")
    logger.Info("binary=dist/app_darwin_amd64")
    logger.DecreasePadding()

    logger.Success("build succeeded")
}

Usage

Basic Logging
logger := bullets.New(os.Stdout)

// By default, all levels use colored bullets (•)
logger.Debug("debug message")    // ○ debug message (dim)
logger.Info("info message")      // • info message (cyan)
logger.Warn("warning message")   // • warning message (yellow)
logger.Error("error message")    // • error message (red)
logger.Success("success!")       // • success! (green)
Formatted Messages
logger.Infof("processing %d items", count)
logger.Warnf("retry %d/%d", current, total)
Structured Logging
// Single field
logger.WithField("user", "john").Info("logged in")

// Multiple fields
logger.WithFields(map[string]interface{}{
    "version": "1.2.3",
    "arch":    "amd64",
}).Info("building package")

// Error field
err := errors.New("connection timeout")
logger.WithError(err).Error("upload failed")
Indentation
logger.Info("main task")
logger.IncreasePadding()
    logger.Info("subtask 1")
    logger.Info("subtask 2")
    logger.IncreasePadding()
        logger.Info("nested subtask")
    logger.DecreasePadding()
logger.DecreasePadding()
Step Function with Timing

The Step function is useful for tracking operations with automatic timing:

done := logger.Step("running tests")
// ... do work ...
done() // Automatically logs completion with duration if > 10s
Spinners

Animated spinners for long-running operations:

// Default Braille spinner (smooth dots)
spinner := logger.Spinner("downloading files")
time.Sleep(3 * time.Second)
spinner.Success("downloaded 10 files")

// Circle spinner (rotating circle)
spinner = logger.SpinnerCircle("connecting to database")
time.Sleep(2 * time.Second)
spinner.Error("connection failed")

// Bounce spinner (bouncing dots)
spinner = logger.SpinnerBounce("processing data")
time.Sleep(2 * time.Second)
spinner.Replace("processed 1000 records")

// Custom frames
spinner = logger.SpinnerWithFrames("compiling", []string{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"})
spinner.Stop() // or spinner.Success(), spinner.Error(), spinner.Replace()
Concurrent Spinners

Multiple spinners can run simultaneously with automatic coordination:

logger := bullets.New(os.Stdout)

// Start multiple operations in parallel
dbSpinner := logger.SpinnerCircle("Connecting to database")
apiSpinner := logger.SpinnerDots("Fetching API data")
fileSpinner := logger.SpinnerBounce("Processing files")

// Each spinner operates independently
go func() {
    time.Sleep(2 * time.Second)
    dbSpinner.Success("Database connected")
}()

go func() {
    time.Sleep(3 * time.Second)
    apiSpinner.Success("API data fetched")
}()

go func() {
    time.Sleep(1 * time.Second)
    fileSpinner.Error("File processing failed")
}()

time.Sleep(4 * time.Second)

Features:

  • Automatic Coordination: SpinnerCoordinator manages all active spinners
  • Thread-Safe: Safe to start/stop spinners from multiple goroutines
  • Smart Line Management: Each spinner gets its own line in TTY mode
  • No Timing Issues: Central animation loop prevents flickering/conflicts
  • Graceful Degradation: Falls back to simple logging in non-TTY environments

How It Works:

  • All spinners share a single coordinator instance
  • Coordinator uses a central ticker (80ms) for smooth animations
  • Channel-based communication ensures thread safety
  • Line numbers automatically recalculated when spinners complete
  • Set BULLETS_FORCE_TTY=1 for reliable TTY detection in go run
Updatable Bullets

Create bullets that can be updated after rendering - perfect for showing progress, updating status, and creating dynamic terminal UIs.

⚠️ Important Terminal Requirements:

The updatable feature requires ANSI escape code support and proper TTY detection. If bullets are not updating in-place (appearing as new lines instead):

  1. Force TTY mode by setting an environment variable:

    export BULLETS_FORCE_TTY=1
    go run your-program.go
    
  2. Why this is needed:

    • go run often doesn't properly detect terminal capabilities
    • Some terminal emulators don't report as TTY correctly
    • IDE integrated terminals may not support ANSI codes
  3. Fallback behavior:

    • When TTY is not detected, updates print as new lines (safe fallback)
    • This ensures your program works in all environments (logs, CI/CD, etc.)
// Create an updatable logger
logger := bullets.NewUpdatable(os.Stdout)

// Create bullets that return handles
handle1 := logger.InfoHandle("Downloading package...")
handle2 := logger.InfoHandle("Installing dependencies...")
handle3 := logger.InfoHandle("Running tests...")

// Update them later
handle1.Success("Package downloaded ✓")
handle2.Error("Dependencies failed ✗")
handle3.Warning("Tests completed with warnings ⚠")

Progress indicators:

download := logger.InfoHandle("Downloading file...")

// Show progress (updates message with progress bar)
for i := 0; i <= 100; i += 10 {
    download.Progress(i, 100)
    time.Sleep(100 * time.Millisecond)
}
download.Success("Download complete!")

Batch operations:

// Group handles for batch updates
h1 := logger.InfoHandle("Service 1")
h2 := logger.InfoHandle("Service 2")
h3 := logger.InfoHandle("Service 3")

group := bullets.NewHandleGroup(h1, h2, h3)
group.SuccessAll("All services running")

// Or use chains
bullets.Chain(h1, h2, h3).
    WithField("status", "active").
    Success("All systems operational")

Adding fields dynamically:

handle := logger.InfoHandle("Building project")

// Add fields as the operation progresses
handle.WithField("version", "1.2.3")
handle.WithFields(map[string]interface{}{
    "arch": "amd64",
    "os": "linux",
})
Customizing Bullets
logger := bullets.New(os.Stdout)

// Enable special bullet symbols (✓, ✗, ⚠, ○)
logger.SetUseSpecialBullets(true)

// Set custom bullet for a specific level
logger.SetBullet(bullets.InfoLevel, "→")
logger.SetBullet(bullets.ErrorLevel, "💥")

// Set multiple custom bullets at once
logger.SetBullets(map[bullets.Level]string{
    bullets.WarnLevel:  "⚡",
    bullets.DebugLevel: "🔍",
})
Log Levels
logger := bullets.New(os.Stdout)
logger.SetLevel(bullets.WarnLevel) // Only warn, error, and fatal will be logged

logger.Debug("not shown")
logger.Info("not shown")
logger.Warn("this is shown")
logger.Error("this is shown")

Available levels:

  • DebugLevel
  • InfoLevel (default)
  • WarnLevel
  • ErrorLevel
  • FatalLevel

Example Output

Default (bullets only):

• building
  • binary=dist/app_linux_amd64
  • binary=dist/app_darwin_amd64
  • binary=dist/app_windows_amd64
• archiving
  • binary=app name=app_0.2.1_linux_amd64
  • binary=app name=app_0.2.1_darwin_amd64
• calculating checksums
• release succeeded

With special bullets enabled:

• building
  • binary=dist/app_linux_amd64
  • binary=dist/app_darwin_amd64
  • binary=dist/app_windows_amd64
• archiving
  • binary=app name=app_0.2.1_linux_amd64
  • binary=app name=app_0.2.1_darwin_amd64
• calculating checksums
✓ release succeeded

With spinners:

⠹ downloading files...    (animating)
• downloaded 10 files     (completed)

Running the Examples

Basic example:

cd examples/basic
go run main.go

Spinner example (including concurrent spinners):

# Recommended: Set environment variable for proper TTY detection
export BULLETS_FORCE_TTY=1
go run examples/spinner/main.go

Updatable bullets example:

# REQUIRED: Set this environment variable for the updates to work properly
export BULLETS_FORCE_TTY=1
go run examples/updatable/main.go

Note: The spinner and updatable features use ANSI escape codes to update lines in place. For best results:

  1. Run in a terminal that supports ANSI codes (most modern terminals)
  2. Set BULLETS_FORCE_TTY=1 environment variable
  3. Run directly in the terminal (not through pipes or output redirection)

These examples demonstrate:

  • basic: Simple logging with bullets and indentation
  • spinner: Animated spinners including concurrent multi-spinner usage
  • updatable: Status updates, progress tracking, batch operations, and parallel operations

API Reference

Logger Methods

Core logging:

  • Debug(msg), Debugf(format, args...)
  • Info(msg), Infof(format, args...)
  • Warn(msg), Warnf(format, args...)
  • Error(msg), Errorf(format, args...)
  • Fatal(msg), Fatalf(format, args...) - logs and exits
  • Success(msg), Successf(format, args...)

Spinners:

  • Spinner(msg) - Default Braille dots spinner
  • SpinnerDots(msg) - Braille dots (same as default)
  • SpinnerCircle(msg) - Rotating circle
  • SpinnerBounce(msg) - Bouncing dots
  • SpinnerWithFrames(msg, frames) - Custom animation

Spinner Control:

  • spinner.Stop() - Stop and clear
  • spinner.Success(msg) - Complete with success
  • spinner.Error(msg) / spinner.Fail(msg) - Complete with error
  • spinner.Replace(msg) - Complete with custom message

Configuration:

  • SetLevel(level), GetLevel()
  • SetUseSpecialBullets(bool) - Enable/disable special symbols
  • SetBullet(level, symbol) - Set custom bullet for a level
  • SetBullets(map[Level]string) - Set multiple custom bullets

Structured logging:

  • WithField(key, value) - Add single field
  • WithFields(map[string]interface{}) - Add multiple fields
  • WithError(err) - Add error field

Indentation:

  • IncreasePadding(), DecreasePadding(), ResetPadding()

Utilities:

  • Step(msg) - Returns cleanup function with timing
UpdatableLogger Methods

Create updatable logger:

  • NewUpdatable(w io.Writer) - Create new updatable logger

Create handle-returning bullets:

  • InfoHandle(msg string) *BulletHandle - Log info and return handle
  • DebugHandle(msg string) *BulletHandle - Log debug and return handle
  • WarnHandle(msg string) *BulletHandle - Log warning and return handle
  • ErrorHandle(msg string) *BulletHandle - Log error and return handle
BulletHandle Methods

Update operations:

  • Update(level Level, msg string) - Update level and message
  • UpdateMessage(msg string) - Update message only
  • UpdateLevel(level Level) - Update level only
  • UpdateColor(color string) - Update color only
  • UpdateBullet(bullet string) - Update bullet symbol only

State transitions:

  • Success(msg string) - Mark as success with message
  • Error(msg string) - Mark as error with message
  • Warning(msg string) - Mark as warning with message

Fields and metadata:

  • WithField(key, value) - Add a field
  • WithFields(fields) - Add multiple fields

Progress tracking:

  • Progress(current, total int) - Show progress bar

State management:

  • GetState() HandleState - Get current state
  • SetState(state HandleState) - Set complete state
HandleGroup Methods
  • NewHandleGroup(handles...) - Create handle group
  • Add(handle) - Add handle to group
  • UpdateAll(level, msg) - Update all handles
  • SuccessAll(msg) - Mark all as success
  • ErrorAll(msg) - Mark all as error
HandleChain Methods
  • Chain(handles...) - Create handle chain
  • Update(level, msg) - Chain update operation
  • Success(msg) - Chain success operation
  • Error(msg) - Chain error operation
  • WithField(key, value) - Chain field addition

Comparison with Other Loggers

This library is designed specifically for CLI applications that need beautiful, human-readable output. It's inspired by:

Unlike general-purpose loggers, bullets focuses on:

  • Visual appeal for terminal output
  • Animated spinners for long operations
  • Customizable bullet symbols
  • Simple API for CLI applications
  • Zero configuration needed
  • No external dependencies

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Acknowledgments

Inspired by the beautiful CLI output of goreleaser and caarlos0/log.

Documentation

Overview

Package bullets provides a colorful terminal logger with bullet-style output. Inspired by goreleaser's logging output.

Index

Constants

View Source
const (
	Reset = reset // Exported version

	// Exported color constants for public use.
	ColorBlack   = black
	ColorRed     = red
	ColorGreen   = green
	ColorYellow  = yellow
	ColorBlue    = blue
	ColorMagenta = magenta
	ColorCyan    = cyan
	ColorWhite   = white

	// Exported bright color constants.
	ColorBrightBlack   = brightBlack
	ColorBrightRed     = brightRed
	ColorBrightGreen   = brightGreen
	ColorBrightYellow  = brightYellow
	ColorBrightBlue    = brightBlue
	ColorBrightMagenta = brightMagenta
	ColorBrightCyan    = brightCyan
	ColorBrightWhite   = brightWhite

	// Exported style constants.
	StyleBold      = bold
	StyleDim       = dim
	StyleItalic    = italic
	StyleUnderline = underline
)

ANSI color codes for terminal output.

Variables

View Source
var ErrInvalidLevel = errors.New("invalid log level")

ErrInvalidLevel is returned when parsing an invalid level string.

Functions

func BatchUpdate

func BatchUpdate(_ []*BulletHandle, updates map[*BulletHandle]struct {
	Level   Level
	Message string
})

BatchUpdate allows updating multiple handles at once. Note: The handles parameter is currently unused but kept for API compatibility.

func Colorize

func Colorize(color, text string) string

Colorize wraps text in ANSI color codes (exported for public use).

Types

type BulletHandle

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

BulletHandle represents a handle to an updatable bullet.

func (*BulletHandle) Error

func (h *BulletHandle) Error(msg string) *BulletHandle

Error updates the bullet to show an error.

func (*BulletHandle) GetState

func (h *BulletHandle) GetState() HandleState

GetState returns the current state of the handle.

func (*BulletHandle) Progress

func (h *BulletHandle) Progress(current, total int) *BulletHandle

Progress updates the bullet to show progress.

func (*BulletHandle) Pulse

func (h *BulletHandle) Pulse(duration time.Duration, alternateMsg string)

Pulse creates a pulsing effect by alternating between two states.

func (*BulletHandle) SetState

func (h *BulletHandle) SetState(state HandleState) *BulletHandle

SetState sets the complete state of the handle.

func (*BulletHandle) Success

func (h *BulletHandle) Success(msg string) *BulletHandle

Success updates the bullet to show success.

func (*BulletHandle) Update

func (h *BulletHandle) Update(level Level, msg string) *BulletHandle

Update updates the bullet with a new message and level.

func (*BulletHandle) UpdateBullet

func (h *BulletHandle) UpdateBullet(bullet string) *BulletHandle

UpdateBullet updates just the bullet symbol.

func (*BulletHandle) UpdateColor

func (h *BulletHandle) UpdateColor(color string) *BulletHandle

UpdateColor updates just the color of the bullet.

func (*BulletHandle) UpdateLevel

func (h *BulletHandle) UpdateLevel(level Level) *BulletHandle

UpdateLevel updates just the level (and thus color/bullet).

func (*BulletHandle) UpdateMessage

func (h *BulletHandle) UpdateMessage(msg string) *BulletHandle

UpdateMessage updates just the message text.

func (*BulletHandle) Warning

func (h *BulletHandle) Warning(msg string) *BulletHandle

Warning updates the bullet to show a warning.

func (*BulletHandle) WithField

func (h *BulletHandle) WithField(key string, value interface{}) *BulletHandle

WithField adds a field to this bullet.

func (*BulletHandle) WithFields

func (h *BulletHandle) WithFields(fields map[string]interface{}) *BulletHandle

WithFields adds multiple fields to this bullet.

type HandleChain

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

HandleChain allows chaining updates to multiple handles.

func Chain

func Chain(handles ...*BulletHandle) *HandleChain

Chain creates a new handle chain.

func (*HandleChain) Error

func (hc *HandleChain) Error(msg string) *HandleChain

Error marks all handles in the chain as error.

func (*HandleChain) Success

func (hc *HandleChain) Success(msg string) *HandleChain

Success marks all handles in the chain as success.

func (*HandleChain) Update

func (hc *HandleChain) Update(level Level, msg string) *HandleChain

Update updates all handles in the chain.

func (*HandleChain) WithField

func (hc *HandleChain) WithField(key string, value interface{}) *HandleChain

WithField adds a field to all handles in the chain.

type HandleGroup

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

HandleGroup manages a group of related handles.

func NewHandleGroup

func NewHandleGroup(handles ...*BulletHandle) *HandleGroup

NewHandleGroup creates a new handle group.

func (*HandleGroup) Add

func (hg *HandleGroup) Add(handle *BulletHandle)

Add adds a handle to the group.

func (*HandleGroup) Clear

func (hg *HandleGroup) Clear()

Clear removes all handles from the group.

func (*HandleGroup) ErrorAll

func (hg *HandleGroup) ErrorAll(msg string)

ErrorAll marks all handles as error.

func (*HandleGroup) Get

func (hg *HandleGroup) Get(index int) *BulletHandle

Get returns the handle at the specified index.

func (*HandleGroup) Size

func (hg *HandleGroup) Size() int

Size returns the number of handles in the group.

func (*HandleGroup) SuccessAll

func (hg *HandleGroup) SuccessAll(msg string)

SuccessAll marks all handles as success.

func (*HandleGroup) UpdateAll

func (hg *HandleGroup) UpdateAll(level Level, msg string)

UpdateAll updates all handles in the group.

func (*HandleGroup) UpdateEach

func (hg *HandleGroup) UpdateEach(updates map[int]struct {
	Level   Level
	Message string
})

UpdateEach updates each handle with a different message.

type HandleState

type HandleState struct {
	Level   Level
	Message string
	Color   string
	Bullet  string
	Fields  map[string]interface{}
}

HandleState represents the state of a bullet handle.

type Level

type Level uint32

Level represents a log level.

const (
	// DebugLevel is the debug level.
	DebugLevel Level = iota
	// InfoLevel is the info level.
	InfoLevel
	// WarnLevel is the warn level.
	WarnLevel
	// ErrorLevel is the error level.
	ErrorLevel
	// FatalLevel is the fatal level.
	FatalLevel
)

func MustParseLevel

func MustParseLevel(s string) Level

MustParseLevel parses a level string or panics.

func ParseLevel

func ParseLevel(s string) (Level, error)

ParseLevel parses a level string into a Level.

func (Level) String

func (l Level) String() string

String returns the string representation of the level.

type Logger

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

Logger represents a logger with configurable level and output.

func Default

func Default() *Logger

Default returns a logger that writes to stderr.

func New

func New(w io.Writer) *Logger

New creates a new logger that writes to the given writer.

func (*Logger) Debug

func (l *Logger) Debug(msg string)

Debug logs a debug message.

func (*Logger) Debugf

func (l *Logger) Debugf(format string, args ...interface{})

Debugf logs a formatted debug message.

func (*Logger) DecreasePadding

func (l *Logger) DecreasePadding()

DecreasePadding decreases the indentation level.

func (*Logger) Error

func (l *Logger) Error(msg string)

Error logs an error message.

func (*Logger) Errorf

func (l *Logger) Errorf(format string, args ...interface{})

Errorf logs a formatted error message.

func (*Logger) Fatal

func (l *Logger) Fatal(msg string)

Fatal logs a fatal message and exits.

func (*Logger) Fatalf

func (l *Logger) Fatalf(format string, args ...interface{})

Fatalf logs a formatted fatal message and exits.

func (*Logger) GetLevel

func (l *Logger) GetLevel() Level

GetLevel returns the current log level.

func (*Logger) IncreasePadding

func (l *Logger) IncreasePadding()

IncreasePadding increases the indentation level.

func (*Logger) Info

func (l *Logger) Info(msg string)

Info logs an info message.

func (*Logger) Infof

func (l *Logger) Infof(format string, args ...interface{})

Infof logs a formatted info message.

func (*Logger) ResetPadding

func (l *Logger) ResetPadding()

ResetPadding resets the indentation to zero.

func (*Logger) SetBullet

func (l *Logger) SetBullet(level Level, bullet string)

SetBullet sets a custom bullet symbol for a specific log level. Custom bullets take priority over special bullets.

func (*Logger) SetBullets

func (l *Logger) SetBullets(bullets map[Level]string)

SetBullets sets custom bullet symbols for multiple log levels. Custom bullets take priority over special bullets.

func (*Logger) SetLevel

func (l *Logger) SetLevel(level Level)

SetLevel sets the minimum log level.

func (*Logger) SetUseSpecialBullets

func (l *Logger) SetUseSpecialBullets(use bool)

SetUseSpecialBullets enables or disables special bullet symbols (✓, ✗, ⚠). When disabled (default), all levels use the circle bullet (●) with level-specific colors.

func (*Logger) Spinner

func (l *Logger) Spinner(msg string) *Spinner

Spinner creates and starts a spinner with default Braille dots animation.

Multiple spinners can run concurrently with automatic coordination. The spinner animates until stopped with Stop(), Success(), Error(), or Replace().

Example:

spinner := logger.Spinner("Processing data")
// ... do work ...
spinner.Success("Processing complete")

In TTY mode, the spinner animates in-place. In non-TTY mode (logs, CI/CD), it displays as a static message.

Thread-safe: Multiple spinners can be created from different goroutines.

func (*Logger) SpinnerBounce

func (l *Logger) SpinnerBounce(msg string) *Spinner

SpinnerBounce creates a spinner with bouncing dot pattern.

Creates a smooth bouncing animation effect using Braille dots that appear to bounce vertically. The animation uses the default speed.

Thread-safe: Multiple spinners can be created from different goroutines.

func (*Logger) SpinnerCircle

func (l *Logger) SpinnerCircle(msg string) *Spinner

SpinnerCircle creates a spinner with growing/shrinking circle pattern.

Creates a glassy circular rotation effect using quarter-circle characters. The animation is slower than the default Braille dots for a more relaxed feel.

Thread-safe: Multiple spinners can be created from different goroutines.

func (*Logger) SpinnerDots

func (l *Logger) SpinnerDots(msg string) *Spinner

SpinnerDots creates a spinner with rotating Braille dots pattern.

This is the default spinner style with smooth dot transitions. Identical to Spinner().

Thread-safe: Multiple spinners can be created from different goroutines.

func (*Logger) SpinnerWithFrames

func (l *Logger) SpinnerWithFrames(msg string, frames []string) *Spinner

SpinnerWithFrames creates and starts a spinner with custom animation frames.

Frames will cycle through the provided slice of strings. If frames is empty, defaults to the standard Braille dots pattern.

Example:

frames := []string{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"}
spinner := logger.SpinnerWithFrames("Compiling", frames)
// ... do work ...
spinner.Success("Compilation complete")

Thread-safe: Multiple spinners can be created from different goroutines.

func (*Logger) Step

func (l *Logger) Step(msg string) func()

Step logs a step message with timing information. It returns a function that should be called when the step is complete.

func (*Logger) Success

func (l *Logger) Success(msg string)

Success logs a success message (using info level with success bullet).

func (*Logger) Successf

func (l *Logger) Successf(format string, args ...interface{})

Successf logs a formatted success message.

func (*Logger) Warn

func (l *Logger) Warn(msg string)

Warn logs a warning message.

func (*Logger) Warnf

func (l *Logger) Warnf(format string, args ...interface{})

Warnf logs a formatted warning message.

func (*Logger) WithError

func (l *Logger) WithError(err error) *Logger

WithError returns a new logger with an error field.

func (*Logger) WithField

func (l *Logger) WithField(key string, value interface{}) *Logger

WithField returns a new logger with the given field added.

func (*Logger) WithFields

func (l *Logger) WithFields(fields map[string]interface{}) *Logger

WithFields returns a new logger with the given fields added.

type Spinner

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

Spinner represents an animated spinner that can be stopped and replaced.

Spinners provide visual feedback for long-running operations. Multiple spinners can run concurrently with automatic coordination via SpinnerCoordinator.

In TTY mode, spinners animate in-place using ANSI escape codes. In non-TTY mode, they display as static messages for compatibility with logs and CI/CD systems.

Example usage:

logger := bullets.New(os.Stdout)
spinner := logger.SpinnerCircle("Processing data")
// ... do work ...
spinner.Success("Processing complete")

Thread Safety:

All spinner methods are thread-safe and can be called from multiple goroutines. The coordinator ensures proper serialization of terminal updates.

func (*Spinner) Error

func (s *Spinner) Error(msg string)

Error stops the spinner and replaces it with an error message.

The completion message is displayed with an error bullet (red color). The bullet symbol respects custom bullets and special bullet settings.

In TTY mode, the spinner line is overwritten with the final message. In non-TTY mode, a new line is printed with the completion message.

Example:

spinner := logger.SpinnerCircle("Connecting")
// ... do work ...
spinner.Error("Connection failed: timeout")

Thread-safe: Can be called from any goroutine.

func (*Spinner) Fail

func (s *Spinner) Fail(msg string)

Fail is an alias for Error.

This method behaves identically to Error() and is provided for convenience. Thread-safe: Can be called from any goroutine.

func (*Spinner) Replace

func (s *Spinner) Replace(msg string)

Replace stops the spinner and replaces it with a custom message at info level.

The completion message is displayed with an info bullet (cyan color). The bullet symbol respects custom bullets and special bullet settings.

Use Replace when the operation completes but you want to show a custom message that doesn't imply success or failure.

Example:

spinner := logger.SpinnerCircle("Processing")
// ... do work ...
spinner.Replace("Processed 1000 records in 5.2s")

Thread-safe: Can be called from any goroutine.

func (*Spinner) Stop

func (s *Spinner) Stop()

Stop stops the spinner and clears the line without displaying a completion message.

This method immediately halts the animation and unregisters the spinner from the coordinator. The spinner cannot be reused after calling Stop().

Thread-safe: Can be called from any goroutine.

func (*Spinner) Success

func (s *Spinner) Success(msg string)

Success stops the spinner and replaces it with a success message.

The completion message is displayed with a success bullet (green color). The bullet symbol respects custom bullets and special bullet settings.

In TTY mode, the spinner line is overwritten with the final message. In non-TTY mode, a new line is printed with the completion message.

Example:

spinner := logger.SpinnerCircle("Connecting")
// ... do work ...
spinner.Success("Connected successfully")

Thread-safe: Can be called from any goroutine.

type SpinnerCoordinator added in v0.2.0

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

SpinnerCoordinator manages all spinner instances and coordinates their output.

The coordinator implements a centralized pattern where a single goroutine handles all spinner animations and updates. This eliminates timing issues and ensures smooth, flicker-free animations even with multiple concurrent spinners.

Architecture:

  • Central ticker goroutine updates all active spinners (80ms interval)
  • Channel-based communication for thread-safe spinner updates
  • Automatic line number allocation and recalculation
  • Unified TTY detection for consistent behavior

The coordinator is automatically created by Logger and managed internally. Users don't need to interact with it directly.

Thread Safety:

All coordinator methods are thread-safe and can be called from multiple goroutines. Internal state is protected by mutexes and channel synchronization.

type UpdatableLogger

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

UpdatableLogger wraps a regular Logger and provides updatable bullet functionality.

func NewUpdatable

func NewUpdatable(w io.Writer) *UpdatableLogger

NewUpdatable creates a new updatable logger.

func (*UpdatableLogger) Debug

func (ul *UpdatableLogger) Debug(msg string)

Debug logs a debug message and increments line count.

func (*UpdatableLogger) DebugHandle

func (ul *UpdatableLogger) DebugHandle(msg string) *BulletHandle

DebugHandle logs a debug message and returns a handle for updates.

func (*UpdatableLogger) Error

func (ul *UpdatableLogger) Error(msg string)

Error logs an error message and increments line count.

func (*UpdatableLogger) ErrorHandle

func (ul *UpdatableLogger) ErrorHandle(msg string) *BulletHandle

ErrorHandle logs an error message and returns a handle for updates.

func (*UpdatableLogger) IncrementLineCount

func (ul *UpdatableLogger) IncrementLineCount()

IncrementLineCount increments the line count (called by regular log methods).

func (*UpdatableLogger) Info

func (ul *UpdatableLogger) Info(msg string)

Info logs an info message and increments line count.

func (*UpdatableLogger) InfoHandle

func (ul *UpdatableLogger) InfoHandle(msg string) *BulletHandle

InfoHandle logs an info message and returns a handle for updates.

func (*UpdatableLogger) Success

func (ul *UpdatableLogger) Success(msg string)

Success logs a success message and increments line count.

func (*UpdatableLogger) Warn

func (ul *UpdatableLogger) Warn(msg string)

Warn logs a warning message and increments line count.

func (*UpdatableLogger) WarnHandle

func (ul *UpdatableLogger) WarnHandle(msg string) *BulletHandle

WarnHandle logs a warning message and returns a handle for updates.

Directories

Path Synopsis
examples
basic command
Package main demonstrates the basic usage of the bullets logger.
Package main demonstrates the basic usage of the bullets logger.
spinner command
Package main demonstrates concurrent spinner usage with the bullets logger.
Package main demonstrates concurrent spinner usage with the bullets logger.
updatable command
Package main demonstrates updatable bullet functionality with real-time updates.
Package main demonstrates updatable bullet functionality with real-time updates.

Jump to

Keyboard shortcuts

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