Documentation
¶
Overview ¶
Package actorevent emits the actor events: the lifecycle events and the usage samples. Log writes both copies of a record, the stdout one and the OTLP one, from a single call, so nothing about a record is kept in step by hand. Ordinary component logs stay on stdout.
This is not an slog bridge. A bridge would put every component record on the wire, cannot set EventName, and would loop, because serverboot routes OTel SDK errors through slog.
serverboot.InitLogging pairs this with a batching processor. These records sit on the actor resume path, so exporting inside Log would put a blocking gRPC call there and make a slow collector look like control-plane latency.
Index ¶
Constants ¶
const ScopeName = "github.com/agent-substrate/substrate/internal/actorevent"
ScopeName is the instrumentation scope of every actor event. A consumer selects a stream by event name.
Variables ¶
var ( StateChanged = Event{ Name: "ate.actor.state_changed", Body: "Actor state changed", Severity: log.SeverityInfo, Keys: append(append([]string{}, identityKeys...), string(ateattr.ActorOperationNameKey), string(ateattr.ActorStateKey)), } Crashed = Event{ Name: "ate.actor.crashed", Body: "Actor crashed", Severity: log.SeverityError, Keys: append(append([]string{}, identityKeys...), string(ateattr.ActorOperationNameKey), string(ateattr.ActorStateKey)), } UsageSampled = Event{ Name: "ate.actor.usage_sampled", Body: "Actor usage sampled", Severity: log.SeverityInfo, Keys: append(append([]string{}, identityKeys...), string(ateattr.WorkerPoolNamespaceKey), string(ateattr.WorkerPoolNameKey), string(ateattr.SandboxClassKey), string(ateattr.StatsSourceKey), string(ateattr.StatsKindKey), string(ateattr.ActorEpochKey)), Conditional: []string{ string(ateattr.StatsMemoryUsageKey), string(ateattr.StatsMemoryPeakKey), string(ateattr.StatsMemoryWorkingSetKey), string(ateattr.StatsCPUTimeKey), }, } )
Three names. A crash is its own because it has a different severity; there is no name per state, because ate.actor.state already says which transition happened. UsageSampled is the ateoms' measurement record.
Functions ¶
func BuildRecord ¶
BuildRecord turns the stdout record into its OTLP form. Attributes carry everything machine-readable, so the body stays the display string.
It sets no trace context: the SDK lifts that from ctx onto the record's own TraceId/SpanId fields.
Types ¶
type Emitter ¶
type Emitter struct {
// contains filtered or unexported fields
}
Emitter writes the OTLP copy through one log.Logger. Tests construct one directly, so emit needs no global provider and can run in parallel.
func NewEmitter ¶
func NewEmitter(lp log.LoggerProvider) *Emitter
NewEmitter writes under ScopeName.
func (*Emitter) LogAt ¶ added in v0.3.0
LogAt writes both copies of ev with t as their timestamp, so a consumer can join them on an exact time. t is when the thing happened: for a usage sample the read time, not the write time. That is why the stdout record is built here rather than through slog.LogAttrs, which would take its own reading.
--log-level=warn silences the stdout copy of an info event while the OTLP copy still ships.
type Event ¶
type Event struct {
Name string
Body string
Severity log.Severity
Keys []string
Conditional []string
}
Event is one name in the closed vocabulary. Name is the LogRecord's own event name field, not an attribute. Body and Severity live here rather than at a call site, so the two copies of a record cannot differ.
Keys is the attribute set the name promises on every record. Conditional holds the keys that are present exactly when the registry's condition holds, and absent otherwise: absence means not measured, zero means measured as zero. The tests hold both lists in step with the registry, and a caller cannot widen the record.