Help us shape the future of Go! Take the 2026 Go Developer Survey

tracer

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package tracer records finished tool invocations as JSON Lines.

Tracer is the seam, shaped like a logger: Log, Warn, Error and Fatal each record an Entry at their level. FileTracer appends a record to a log file and echoes it to a writer, and Nop records nothing. A tracer never fails the caller: a record that cannot be written is reported on the warning writer only.

Index

Examples

Constants

View Source
const (
	LevelInfo  = "info"
	LevelWarn  = "warn"
	LevelError = "error"
	LevelFatal = "fatal"
)

Level of a record.

View Source
const (
	StdinTerminal = "terminal"
	StdinPipe     = "pipe"
)

Values for Config.Stdin.

View Source
const FileName = ".gotools_trace.log"

FileName is the conventional trace log, in the project root.

Variables

This section is empty.

Functions

func Code

func Code(code int) *int

Code returns a pointer to an exit code, for Entry.Code.

func ReproCommand

func ReproCommand(program, tool string, args []string) string

ReproCommand is the copy-pasteable invocation for a trace record.

func ShellQuote

func ShellQuote(arg string) string

ShellQuote renders arg so a shell reads it back as one word.

func StdinKind

func StdinKind(r io.Reader) string

StdinKind reports whether r is a terminal or a pipe, as a Config.Stdin value.

Types

type Config

type Config struct {
	Path    string    // the log file; records are appended to it
	Echo    io.Writer // each record line is also written here; nil for none
	Warn    io.Writer // where a failure to trace is reported; nil for none
	Program string    // the command name used in the repro command
	Stdin   string    // StdinTerminal or StdinPipe
	Env     Env
	Now     func() time.Time // the clock; time.Now when nil
}

Config is what a FileTracer stamps on every record.

type Entry

type Entry struct {
	Tool    string
	Binary  string   // the binary a tool resolved to
	Args    []string // the arguments the tool was run with
	Code    *int     // the tool's exit code; set for a finished invocation
	Message string   // what happened, for warnings, errors and fatal stops
}

Entry is what a caller reports. The tracer adds the timestamp and the configuration the process runs under.

type Env

type Env struct {
	Strategy     string `json:"GOTOOLS_STRATEGY"`
	Dir          string `json:"GOTOOLS_DIR"`
	GoVersion    string `json:"GOTOOLS_GO_VERSION"`
	ModulePrefix string `json:"GOTOOLS_MODULE_PREFIX"`
	Verbose      string `json:"GOTOOLS_VERBOSE"`
	Trace        string `json:"GOTOOLS_TRACE"`
}

Env is the resolved configuration a record was produced under.

type FileTracer

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

FileTracer is the Tracer that appends JSON Lines to a file.

Example

A FileTracer stamps every record with the configuration it runs under and echoes the line it appends.

package main

import (
	"os"
	"path/filepath"
	"time"

	"github.com/piusalfred/gotools/internal/tracer"
)

func main() {
	dir, _ := os.MkdirTemp("", "tracer-example")
	defer os.RemoveAll(dir)

	tr := tracer.NewFile(tracer.Config{
		Path:    filepath.Join(dir, tracer.FileName),
		Echo:    os.Stdout,
		Program: "gotools",
		Stdin:   tracer.StdinPipe,
		Env:     tracer.Env{Strategy: "split", Dir: "tools"},
		Now:     func() time.Time { return time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC) },
	})

	tr.Log(tracer.Entry{Tool: "fmt", Binary: "gofumpt", Args: []string{"-w", "."}, Code: new(0)})

}
Output:
{"ts":"2026-01-02T03:04:05Z","level":"info","tool":"fmt","binary":"gofumpt","cmd":"gotools exec fmt -w .","strategy":"split","stdin":"pipe","exit_code":0,"args":["-w","."],"env":{"GOTOOLS_STRATEGY":"split","GOTOOLS_DIR":"tools","GOTOOLS_GO_VERSION":"","GOTOOLS_MODULE_PREFIX":"","GOTOOLS_VERBOSE":"","GOTOOLS_TRACE":""}}

func NewFile

func NewFile(cfg Config) *FileTracer

NewFile returns a FileTracer for cfg. It touches nothing until a record is written.

func (*FileTracer) Error

func (t *FileTracer) Error(e Entry)

Error implements Tracer.

func (*FileTracer) Fatal

func (t *FileTracer) Fatal(e Entry)

Fatal implements Tracer.

func (*FileTracer) Log

func (t *FileTracer) Log(e Entry)

Log implements Tracer.

func (*FileTracer) Warn

func (t *FileTracer) Warn(e Entry)

Warn implements Tracer.

type Nop

type Nop struct{}

Nop is the Tracer that records nothing.

func (Nop) Error

func (Nop) Error(Entry)

func (Nop) Fatal

func (Nop) Fatal(Entry)

func (Nop) Log

func (Nop) Log(Entry)

func (Nop) Warn

func (Nop) Warn(Entry)

type Record

type Record struct {
	TS       string   `json:"ts"`
	Level    string   `json:"level"`
	Tool     string   `json:"tool"`
	Binary   string   `json:"binary,omitzero"`
	Cmd      string   `json:"cmd,omitzero"`
	Error    string   `json:"error,omitzero"` // Entry.Message
	Strategy string   `json:"strategy"`
	Stdin    string   `json:"stdin"`
	ExitCode *int     `json:"exit_code,omitzero"`
	Args     []string `json:"args,omitzero"`
	Env      Env      `json:"env"`
}

Record is one line of the trace log. It is built from fields, never by string assembly, so every value is escaped by the encoder.

type Tracer

type Tracer interface {
	Log(Entry)
	Warn(Entry)
	Error(Entry)
	Fatal(Entry)
}

Tracer records what a command did, one level per method like a logger. Fatal records that gotools stopped; it does not exit.

Example

Callers hold the interface and swap the implementation.

package main

import (
	"fmt"
	"os"

	"github.com/piusalfred/gotools/internal/tracer"
)

func main() {
	var tr tracer.Tracer = tracer.Nop{}

	if os.Getenv("GOTOOLS_EXAMPLE_TRACE") != "" {
		tr = tracer.NewFile(tracer.Config{Path: tracer.FileName, Echo: os.Stderr})
	}

	tr.Log(tracer.Entry{Tool: "fmt", Binary: "gofumpt", Args: []string{"-w", "."}, Code: new(0)})
	fmt.Println("done")

}
Output:
done

Jump to

Keyboard shortcuts

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