SkillAgentSearch skills...

Dlg

Printf-Style Debugging with Zero-Cost in Production Builds

Install / Use

/learn @vvvvv/Dlg
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<div align="center"> <h1>dlg <a href="https://github.com/vvvvv/dlg/actions/workflows/tests.yml"><img src="https://github.com/vvvvv/dlg/actions/workflows/tests.yml/badge.svg?branch=main" /></a></h1> <h3><em>delog - /diːˈlɑːɡ/ </em></h3> <h3>Printf-Style Debugging with Zero-Cost in Production Builds</h3> </div>

dlg provides a minimal API for printf-style debugging - a lightweight logger that completely vanishes from production builds while providing rich debugging capabilities during development.
When built without the dlg tag, all logging calls disappear entirely from your binary, resulting in no runtime overhead.

Why dlg?

  • 🚀 True zero-cost abstraction - Logging calls completely disappear from production binaries
  • ⚡️ Near-zero overhead - Performance-focused design for debug builds
  • 🔍 Smart stack traces - Runtime-configurable stack trace generation
  • 🔒 Concurrency-safe by design - Custom writers simply implement sync.Locker to be safe
  • Minimalist API - Only Printf, and a couple of utility functions
  • 🎨 Colorize Output - Highlight output for better visibility in noisy output

The Magic of Zero-Cost

When compiled without the dlg build tag:

  • All calls to dlg compile to empty functions
  • Go linker completely eliminates these no-ops
  • Final binary contains no trace of logging code
  • Zero memory overhead, zero CPU impact

For the full technical breakdown, see True Zero-Cost Elimination.

Getting Started

go get github.com/vvvvv/dlg
package main

import (
	"fmt"

	"github.com/vvvvv/dlg"
)

func risky() error {
	return fmt.Errorf("unexpected error")
}

func main() {
	fmt.Println("starting...")

	dlg.Printf("executing risky operation")
	err := risky()
	if err != nil {
		dlg.Printf("something failed: %v", err)
	}

	dlg.Printf("continuing")
}

Activating Debug Mode

Enable debug features with the dlg build tag:

# Production build (no logging)
go build -o app

# Debug build 
go build -tags dlg -o app-debug

Normal Output

$ go build -o app
./app
starting...

Debug Build Output

go build -tags dlg -o app-debug
./app-debug
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * *  DEBUG BUILD  * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- DLG_STACKTRACE=ERROR   show stack traces on errors
- DLG_STACKTRACE=ALWAYS  show stack traces always
- DLG_NO_WARN=1          disable this message (use at your own risk)

starting...
01:28:27 [2µs] main.go:16: executing risky operation
01:28:27 [21µs] main.go:19: something failed: unexpected error
01:28:27 [23µs] main.go:22: continuing

Stack Trace Output

go build -tags dlg -o app-debug
DLG_STACKTRACE=ERROR ./app-debug
# [Debug Banner omitted]
starting...
01:31:34 [2µs] main.go:16: executing risky operation
01:31:34 [21µs] main.go:19: something failed: unexpected error
main.main()
    /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example01/main.go:19 +0xc0
01:31:34 [38µs] main.go:22: continuing

Tracing Regions <sup>experimental</sup>

Sometimes you only want stack traces for a specific area of your code while investigating an issue. Tracing regions let you define those boundaries.

dlg.StartTrace() begins a tracing region, and dlg.StopTrace() ends it.
When DLG_STACKTRACE is set to REGION,ALWAYS, dlg.Printf will print a stack trace only if the current call stack contains a function that's inside an active tracing region.
Similarly, when set to REGION,ERROR, stack traces are printed inside tracing regions only if an error is passed to dlg.Printf.

Basic Usage

The simplest usage is to start and stop a trace around the code you want to inspect.
Any dlg.Printf calls made in that tracing region will include stack traces.

Let's start with the most basic example:

func main(){
    dlg.StartTrace()
    dlg.Printf("foobar")
    dlg.StopTrace()
}

Output DLG_STACKTRACE=REGION,ALWAYS

16:14:39 [10µs] main.go:8: foobar
main.main()
    /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:8 +0x4b

Tracing Across Functions

Tracing isn't limited to a single function. Once a tracing region is started, it covers all functions called within that region until it's stopped. This means you can start tracing in main() and see traces for calls deeper in the stack.


func foo(){
    dlg.Printf("hello from foo")
}

func main(){
    dlg.Printf("outside of tracing region")

    dlg.StartTrace()
    dlg.Printf("started tracing")
    foo()
    dlg.StopTrace()
}

Output DLG_STACKTRACE=REGION,ALWAYS

16:19:35 [1µs] main.go:11: outside of tracing region
16:19:35 [30µs] main.go:14: started tracing
main.main()
        /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:14 +0x67
16:19:35 [43µs] main.go:7: hello from foo
main.foo()
        /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:7 +0x87
main.main()
        /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:15 +0x68

Error Tracing

If you only want stack traces when an error occurs inside a tracing region, set DLG_STACKTRACE=REGION,ERROR. In this mode, traces appear only for dlg.Printf calls that include an error argument.


func main(){
    dlg.StartTrace()

    dlg.Printf("starting...")

    err := fmt.Errorf("this is an error") 

    dlg.Printf("oh no an error: %v", err)

    dlg.StopTrace()
}


Output DLG_STACKTRACE=REGION,ERROR

16:24:20 [3µs] main.go:15: starting...
16:24:20 [29µs] main.go:19: oh no an error: this is an error
main.main()
    /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:19 +0x97

Understanding Tracing Region Scopes

A tracing region is tied to the function scope that called StartTrace. If you call StopTrace() inside a nested function, the tracing region remains active as the region was started from the outer scope.

func main(){
    dlg.StartTrace()

    dlg.Printf("starting...")

    fn := func(){
        dlg.Printf("hello from fn")

        dlg.StopTrace()
    }

    fn()

    dlg.Printf("this will still produce a stack trace")

    dlg.StopTrace()
}

Output DLG_STACKTRACE=REGION,ALWAYS

16:28:27 [12µs] main.go:15: starting...
main.main()
        /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:15 +0x4b
16:28:27 [43µs] main.go:18: hello from fn
main.main.func1()
        /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:18 +0x6b
main.main()
        /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:22 +0x4c
16:28:27 [49µs] main.go:24: this will still produce a stack trace
main.main()
        /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:24 +0x9f

Stopping a Tracing Region from Another Function

To close a tracing region from a nested function, you need to start and stop it with a matching key. This allows you to end exactly the tracing region you intended, even from different scopes.

func main(){
    dlg.StartTrace(1)

    dlg.Printf("starting...")

    fn := func(){
        dlg.Printf("hello from fn")

        dlg.StopTrace(1)
    }

    fn()

    dlg.Printf("this won't trace")
}

Output DLG_STACKTRACE=REGION,ALWAYS

16:34:07 [9µs] main.go:15: starting...
main.main()
        /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:15 +0x6b
16:34:07 [33µs] main.go:18: hello from fn
main.main.func1()
        /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:18 +0x8b
main.main()
        /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:23 +0x6c
16:34:07 [48µs] main.go:25: this won't trace

Choosing a Key

You can use any type as a tracing key: integers, strings, floats, even structs. For clarity, it's best to keep keys simple, such as short strings or integers.


dlg.StartTrace("foo")
...
dlg.StopTrace("foo")


dlg.StartTrace(7.2)
...
dlg.StopTrace(7.2)


dlg.StartTrace(struct{name string}{name: "tracing region"})
...

dlg.StopTrace(struct{name string}{name: "tracing region"})

Stopping Without a Key

StopTrace() without arguments will end the most recent active tracing region, even if it was started with a key - as long as you call it from the same scope.

func main(){
    dlg.StartTrace(1)

    dlg.Printf("this will trace")

    dlg.StopTrace()

    dlg.Printf("this won't trace")
}

Output DLG_STACKTRACE=REGION,ALWAYS

16:47:04 [10µs] main.go:14: this will trace
main.main()
        /Users/v/src/go/src/github.com/vvvvv/dlg/examples/example08/main.go:14 +0x6b
16:47:04 [34µs] main.go:18: this won't trace

💡 All tracing regions, whether keyed or not, are closed in LIFO (last-in, first-out) order.

⚠️ Why You Should Avoid defer StopTrace()

It might be tempting to wrap dlg.StopTrace() in a defer, but don't. The Go compiler cannot eliminate defer calls. Even something as trivial as defer func(){}() remains as a real function call in the compiled binary. If you want true zero-cost elimination, call StopTrace directly.

For more examples of tracing regions, see /tests/stacktraceregion/region_test.go.

Concurrency Safety for Custom Writers

While dlg.Printf is safe for concurrent use, custom writers should implement sync.Locker.

package main

import (
	"bytes"
	"fmt"
	"sync"

	"github.com/vvvvv/dlg"
)

type SafeBuffer struct {
	bytes.Buffer
	sync.Mutex
}

func main() {
	sb := &SafeBuffer{}
	dlg.SetOutput(sb) // Now fully concurrency-safe!

	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for n := 0; n < 5; n++ {
				dlg.Printf("from goroutine #%v: message %v
View on GitHub
GitHub Stars166
CategoryDevelopment
Updated4mo ago
Forks2

Languages

Go

Security Score

92/100

Audited on Nov 18, 2025

No findings