README
ΒΆ
GoPulse β Goroutine Visualizer & Debugger
A modern toolkit for visualizing, tracing, and debugging goroutine lifecycles and channel usage in Go applications.
- Visualize goroutine lifecycles and channel operations in real-time
- Trace blocking, leaks, deadlocks, and contention with minimal overhead
- Debug complex concurrency issues with interactive tools (CLI + web UI)
- Zero dependencies and easy to instrument existing code
Table of Contents
- Screenshot
- Quick Start
- Installation
- Basic Usage
- Configuration
- CLI Commands
- Web UI
- In-App Live UI
- Examples
- Environment Variables
- Cross-Compilation
- Troubleshooting
- Compatibility
- Roadmap
- Contributing
- License
Screenshots

A live snapshot of the in-app UI showing goroutine states, channel activity, and performance summaries.
Quick Start
Get up and running in under 30 seconds:
package main
import (
"context"
"time"
"github.com/cploutarchou/gopulse/trace"
)
func main() {
// Start tracing
_ = trace.Start(trace.Config{Output: "stdout"})
defer trace.Stop()
// Start live UI server
stop, _ := trace.UI(":8080")
defer stop(context.Background())
// Your concurrent code here...
ch := trace.NewChan[int](0, "demo")
trace.Go(func() {
defer ch.Close()
for i := 0; i < 10; i++ {
ch.Send(i)
time.Sleep(100 * time.Millisecond)
}
})
trace.Go(func() {
for v, ok := ch.Recv(); ok; v, ok = ch.Recv() {
_ = v // process value
}
})
// Open http://localhost:8080 to see live activity
time.Sleep(2 * time.Second)
}
Installation
Requirements: Go 1.24+ (Go 1.20+ supported)
Install the CLI tool:
go install github.com/cploutarchou/gopulse/cmd/gopulse@latest
Or pin to a specific version:
go install github.com/cploutarchou/gopulse/cmd/gopulse@v1.0.0
Add the library to your project:
go get github.com/cploutarchou/gopulse
Basic Usage
1. Instrument Your Code
Import and initialize tracing in your application:
import "github.com/cploutarchou/gopulse/trace"
func main() {
// Start tracing to stdout (or specify a file path)
_ = trace.Start(trace.Config{Output: "stdout"})
defer trace.Stop()
// Your application code...
}
2. Instrument Goroutines and Channels
Use trace.Go() for goroutines and trace.NewChan() for channels:
// Traced goroutines
trace.Go(func() {
// goroutine work
})
// Traced channels
ch := trace.NewChan[string](10, "worker-queue")
ch.Send("message")
value, ok := ch.Recv()
ch.Close()
// Blocking operations
unblock := trace.Block("database query")
result := database.Query("SELECT ...")
unblock()
3. Collect Events
Run your application and collect trace events:
# Output to file
go run your-app.go > events.log
# Or configure file output in code
_ = trace.Start(trace.Config{Output: "trace.log"})
Sample JSON event:
{"time":"2025-01-01T12:00:00Z","type":"g_start","gid":42,"details":{"real_gid":123}}
Configuration
trace.Config Options
type Config struct {
// Output destination: "stdout", "-", or file path
Output string
// Enable block/unblock event sampling (default: true)
SampleBlocks bool
}
// Examples
_ = trace.Start(trace.Config{
Output: "/tmp/myapp-trace.log",
SampleBlocks: true,
})
Event Types
- / - Goroutine lifecycle
g_start``g_stop - / / - Channel operations
c_send``c_recv``c_close block/ - Blocking pointsunblock-
- Potential deadlock detection
deadlock_hint
- Potential deadlock detection
CLI Commands
Analyze Events
# Basic analysis and leak detection
gopulse analyze events.log
# Get help
gopulse analyze --help
Output example:
Found 3 goroutines in log
G#1: 2025-01-01T12:00:00Z -> 2025-01-01T12:00:01Z (finished)
- 2025-01-01T12:00:00Z g_start
- 2025-01-01T12:00:00Z c_send chan=demo
- 2025-01-01T12:00:01Z g_stop
Potential leaks (goroutines without stop): 0
Web Visualization
# Static view (refresh to reload)
gopulse web events.log -addr :8080
# Live streaming view (auto-updates)
gopulse web events.log -addr :8080 -live
# Custom address
gopulse web events.log -addr localhost:3000 -live
Web UI features:
- Static mode: Timeline visualization with refresh-to-reload
- Live mode: Real-time table view with SSE streaming
- Goroutine status tracking (running/blocked/finished)
- Channel operation counters
- Automatic deadlock hints
Web UI
Static Timeline View
- Visual timeline of goroutine lifecycles
- Channel event annotations
- Manual refresh to reload data
- Ideal for post-mortem analysis
Live Dashboard
- Real-time goroutine status table
- Channel activity counters
- Automatic updates via Server-Sent Events
- Deadlock detection alerts
- Summary statistics
Access at after starting the web server. http://localhost:8080
In-App Live UI
Embed a live web UI directly in your application:
import (
"context"
"github.com/cploutarchou/gopulse/trace"
)
func main() {
// Start tracing
_ = trace.Start(trace.Config{Output: "stdout"})
defer trace.Stop()
// Start embedded web server
stop, err := trace.UI(":8080")
if err != nil {
log.Fatal(err)
}
defer stop(context.Background())
// Your application logic...
// UI will be available at http://localhost:8080
select {} // keep alive
}
Benefits:
- No external log files required
- Real-time streaming from memory
- Zero-configuration setup
- Production-safe with low overhead
Examples
The project includes several complete examples:
UI Example (Embedded Web UI)
go run ./examples/ui
# Open http://localhost:8080
CLI Example (File-based Analysis)
go run ./examples/cli > events.log
gopulse analyze events.log
gopulse web -addr :8080 -live events.log
# Open http://localhost:8080
Advanced Instrumentation
// Custom channel names for easier debugging
userQueue := trace.NewChan[User](100, "user-processing-queue")
resultChan := trace.NewChan[Result](0, "results")
// Named blocking operations
unblock := trace.Block("external API call")
response := httpClient.Get("https://api.example.com")
unblock()
// Traced goroutine pools
for i := 0; i < 10; i++ {
trace.Go(func() {
for user := range userQueue.Raw() {
result := processUser(user)
resultChan.Send(result)
}
})
}
Troubleshooting
Common Issues
No events appearing:
- Verify
trace.Start()is called before any traced operations - Check output destination (stdout vs file permissions)
- Ensure is executed
defer trace.Stop()
Web UI shows empty/stale data:
- For live mode, verify the log file is being actively written
- Check file permissions for the events log
- Try refreshing the browser (static mode)
Port conflicts:
- Change the flag:
gopulse web -addr :9090 events.log-addr - For in-app UI:
trace.UI(":9090")
High overhead concerns:
- GoPulse is designed for low overhead (~1-5% in typical workloads)
- Disable runtime integration: unset
GOPULSE_RUNTIME_TRACE - Use file output instead of stdout in production
Permission errors:
# Ensure write permissions for output directory
mkdir -p logs && chmod 755 logs
export GOPULSE_OUTPUT=logs/trace.log
Performance Impact
GoPulse is designed for production use with minimal overhead:
- CPU impact: ~1-3% in typical concurrent workloads
- Memory impact: Small fixed overhead + event buffer (~1MB)
- I/O impact: Asynchronous JSON writes, buffered output
- No telemetry: All data stays local
Compatibility
| Go Version | Status | Notes |
|---|---|---|
| 1.24+ | β Recommended | Full feature support |
| 1.23 | β Supported | All features work |
| 1.22 | β Supported | All features work |
| 1.21 | β Supported | All features work |
| 1.20 | β οΈ Limited | Basic features only |
| <1.20 | β Not supported | Use Go 1.20+ |
| Platform support: |
- β Linux (AMD64, ARM64)
- β macOS (Intel, Apple Silicon)
- β Windows (AMD64, ARM64)
- β FreeBSD, OpenBSD (community tested)
Roadmap
Completed β
- Goroutine start/stop tracing
- Channel send/receive/close tracing
- Basic deadlock and leak detection
- CLI analysis and web visualization
- In-app live UI server
- Cross-platform builds
In Progress π§
- Enhanced deadlock detection algorithms
- Performance profiling integration
- Goroutine stack trace capture
Planned π
- Rich interactive web UI with graphs and filtering
- VS Code extension for trace visualization
- Delve debugger integration
- Trace diff and comparison tools
- Export to OpenTelemetry format
- Integration with popular observability platforms
Contributing
Contributions are welcome! Here's how to get started:
- Fork the repository
- a feature branch:
git checkout -b feature/amazing-featureCreate - Make your changes and add tests
- Run tests:
go test ./... - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - a Pull Request Open
Development Setup
git clone https://github.com/cploutarchou/gopulse.git
cd gopulse
go mod tidy
go test ./...
Reporting Issues
Please use GitHub Issues for:
- π Bug reports
- π‘ Feature requests
- π Documentation improvements
- β Usage questions
License
MIT License - see LICENSE file for details. Copyright (c) 2025 Christos Ploutarchou
Star β this repo if gopulse helps you debug concurrent Go applications!