Files
zooqueenandHanzo Dev dbf00910f1 spans and logs travel the way metrics already do: native batches over ZAP
One protocol for the three signals, shapes declared once in internal/otlz and
pinned both directions against what the receiver accepts — a field invented
fails as loudly as a field missed. No OpenTelemetry SDK is linked by otlz;
importing the receiver for tests would have grown the module graph to 1145
modules, so the contract is goldens, measured not assumed.

Zero configuration. A program reports because it exists: each signal resolves
its collector by convention — an override if one is set, the well-known socket
if it exists, loopback's conventional port otherwise — re-resolved on every
attempt, so a collector that starts later is found without a restart. Absence
is free: lazy dial, the batch dropped, one line when export begins working and
one when it stops, with the reason. Over the socket the kernel attests which
process exported a batch, which loopback cannot.

Two races the tests forced out: the ship decision moved off the serve
goroutine, so a request answered immediately after Serve no longer loses its
span; and export starts in the last synchronous step Serve and Listen share,
so a short-lived process cannot reach Shutdown before the flush hook exists.

The runtime-directory scheme lives once in internal/sock; RuntimeDir and
SocketPath delegate to it. luxfi/zap moves one patch, v1.2.6 to v1.2.7.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-04 16:51:09 -07:00

53 lines
2.1 KiB
Go

// Package sock is the fleet's socket-path scheme, in one place.
//
// A service NAME maps to exactly one path — <Dir()>/<name>.sock — and both
// halves of every connection use this mapping, which is what keeps them from
// drifting: a service serves at that path and a caller dials it by name, with no
// registry and no discovery service in between.
//
// It lives down here, below zip, because zip is not the only caller any more.
// zip.RuntimeDir and zip.SocketPath are the public spelling of it, and the
// telemetry exporter needs the same answer without importing zip — which it
// cannot do, since zip imports the exporter. A second copy of the rule would be
// a fleet where a program serves at one path and its telemetry looks for another.
package sock
import (
"os"
"path/filepath"
)
// DirEnv names the directory holding one socket per service. It is the one knob
// in the scheme.
const DirEnv = "ZIP_RUNTIME_DIR"
// Dir is the directory that holds the fleet's sockets, resolved in one order:
//
// $ZIP_RUNTIME_DIR set explicitly — always wins
// $XDG_RUNTIME_DIR/zip a developer's per-user runtime dir (/run/user/1000)
// /run/zip the system default
//
// The middle case is what makes a dev box work without configuration: /run/zip
// is not writable by a normal user and $XDG_RUNTIME_DIR is.
//
// The environment is read on every call rather than cached, so a process that
// arranges its own runtime directory after start — and a test that does — sees
// the directory it actually has.
func Dir() string {
if d := os.Getenv(DirEnv); d != "" {
return d
}
if d := os.Getenv("XDG_RUNTIME_DIR"); d != "" {
return filepath.Join(d, "zip")
}
return "/run/zip"
}
// Path is the socket a service of this name is reached at.
func Path(name string) string { return In(Dir(), name) }
// In is the shape of every socket path, stated once: the plugin loader names a
// child's private socket with it too, so "a service's socket is
// <dir>/<name>.sock" has one definition.
func In(dir, name string) string { return filepath.Join(dir, name+".sock") }