Observer
The observer interface allows you to track VM execution for profiling, debugging, and auditing. An observer receives callbacks as the virtual machine executes bytecode instructions, giving you visibility into script behavior without modifying the script itself.
Attaching an Observer
Pass an observer to Eval, Compile, or Run using the WithObserver
option:
result, err := risor.Eval(ctx, source,
risor.WithEnv(risor.Builtins()),
risor.WithObserver(myObserver),
)The Observer Interface
An observer implements the vm.Observer interface. The VM calls the observer
on each instruction step, providing information about the current execution
state.
type Observer interface {
Step(state *vm.State)
}Use Cases
Profiling
Count instructions, measure execution time per function, or identify hot paths in a script.
type Profiler struct {
steps int64
}
func (p *Profiler) Step(state *vm.State) {
p.steps++
}After execution, p.steps contains the total number of instructions executed.
Debugging
Log or inspect the VM state at each step. This is useful for building step-through debuggers or execution traces.
type Tracer struct {
log []string
}
func (t *Tracer) Step(state *vm.State) {
t.log = append(t.log, fmt.Sprintf("ip=%d", state.IP))
}Auditing
Track which functions or operations a script invokes. This can be used to enforce security policies or generate audit logs.
Combining with Other Options
The observer works alongside all other options. A common pattern is to combine an observer with resource limits for both visibility and safety:
profiler := &Profiler{}
result, err := risor.Eval(ctx, source,
risor.WithEnv(risor.Builtins()),
risor.WithObserver(profiler),
risor.WithMaxSteps(100000),
risor.WithTimeout(5 * time.Second),
)
fmt.Printf("executed %d instructions\n", profiler.steps)Performance Considerations
The observer is called on every instruction step, so the callback should be
lightweight. Avoid blocking operations, heavy allocations, or synchronization
in the Step method. For high-throughput use cases, consider sampling (e.g.
only recording every Nth step) or batching observations for later analysis.