Context
Work with the central runtime context after the application starts.
runtime.Context is the application boundary passed to runtime extensions. It
embeds context.Context, exposes the environment, gives access to the component
container, and owns refresh/close lifecycle behavior.
The interface shows what the runtime context is responsible for:
type Context interface {
// context.Context provides cancellation and scoped values.
context.Context
// Lifecycle gives the runtime context start and stop behavior.
Lifecycle
// Environment returns profiles, property sources, and property resolution.
Environment() Environment
// Container returns the component container for resolving or inspecting components.
Container() component.Container
// ResourceResolver returns the shared runtime resource resolver.
ResourceResolver() io.ResourceResolver
// Refresh prepares the container and initializes the component graph.
Refresh(ctx context.Context) error
// Close stops lifecycle components and disposes initialized singletons.
Close(ctx context.Context) error
}context.Context gives runtime-aware code cancellation and request-scoped
signals in the usual Go shape.
Lifecycle means the context has start and stop behavior. The application calls
that lifecycle while booting and closing the process.
Environment() returns profiles and configuration sources that were prepared
before component creation.
Container() returns the component container for framework-level code that must
inspect or dynamically resolve components.
ResourceResolver() returns the shared resolver used for runtime resources such
as configuration files.
Refresh() builds the application container and initializes the component graph.
Close() stops lifecycle objects and disposes initialized singletons.
Use it when code is part of the runtime flow: command-line runners, context initializers, lifecycle resources, framework packages, and integration code that needs to inspect application state.
type DiagnosticsRunner struct{}
func (r *DiagnosticsRunner) Run(ctx runtime.Context, args *runtime.Args) error {
env := ctx.Environment()
container := ctx.Container()
resolver := ctx.ResourceResolver()
configResource, err := resolver.Resolve(ctx, "application.yaml")
if err != nil {
return err
}
slog.Info("runtime diagnostics",
"profiles", env.ActiveProfiles(),
"components", len(container.DefinitionNames()),
"hasConfig", configResource.Exists(),
)
return nil
}Context initializer
Use a runtime.ContextInitializer when you need to customize the context before
it is refreshed and used by the application.
type ObservabilityInitializer struct{}
func NewObservabilityInitializer() *ObservabilityInitializer {
return &ObservabilityInitializer{}
}
func (i *ObservabilityInitializer) InitializeContext(ctx runtime.Context) error {
enabled := ctx.Environment().
PropertyResolver().
LookupOrDefault("observability.enabled", false)
if enabled == true {
slog.Info("observability is enabled")
}
return nil
}Register the initializer as a component:
func init() {
component.Register(NewObservabilityInitializer)
}Refresh and close
The application calls Refresh during startup. Refresh creates the application
container, loads component definitions, runs container customizers, registers
initialization processors, initializes singletons, resolves the lifecycle
manager, and starts lifecycle components.
Close stops lifecycle components, destroys initialized singletons, and marks
the context as canceled.
Most applications let Application.Run call these methods. Extension code
should only call Refresh or Close directly in tests or when building
framework-level runtime packages.
Runtime values available for injection
During refresh, Procyon registers core runtime values so constructors can depend on them:
func NewDiagnostics(
ctx runtime.Context,
env runtime.Environment,
resolver io.ResourceResolver,
) *Diagnostics {
return &Diagnostics{ctx: ctx, env: env, resolver: resolver}
}Use these dependencies for infrastructure and framework integration code. For normal business services, prefer passing only the specific services or typed configuration they actually need.
