Debugging / Introspection
Every Kontainer is fully introspectable. Query the complete service graph at runtime — what is registered, how it's wired, what overrides what, and whether instances have been created.
Dumping the container
For a quick overview, dumpKontainer() returns a human-readable table of all services, their types,
and current instances:
val kontainer = blueprint.create()
println(kontainer.tools.dumpKontainer())
// Kontainer dump:
//
// Service ID | Type | Instances
// Counter | Singleton |
// MyService | Singleton |
// RequestContext | Dynamic | 2024-01-15T10:30:00Z: RequestContext Structured debug info
For programmatic access, getDebugInfo() returns the full service graph as structured data. Each
service entry includes its type, what it creates, what it injects, and whether it overrides another definition:
val debugInfo = kontainer.tools.getDebugInfo()
debugInfo.services.forEach { service ->
println("${service.cls.fqn} [type=${service.type}]")
// What does this service inject?
service.definition.injects.forEach { param ->
val lazy = if (param.provisionType == DebugInfo.ParamInfo.ProvisionType.Lazy) " (lazy)" else ""
println(" -> ${param.name}: ${param.classes.map { it.fqn }}${lazy}")
}
// Does this service override another definition?
service.definition.overwrites?.let { overridden ->
println(" overrides: ${overridden.creates.fqn}")
}
} Service types
Each service in the debug info carries a type that tells you exactly how the container manages it:
| Type | Meaning |
|---|---|
Singleton | Shared across all container instances created from the same blueprint |
Dynamic | Lives only within a single container instance |
SemiDynamic | Defined as singleton but injects a dynamic service, so promoted automatically |
Prototype | New instance created for every injection |
DynamicOverride | A dynamic service overridden at container creation time |
Querying the provider factory
For lower-level access, the tools.factory exposes the raw service providers:
val factory = kontainer.tools.factory
// All registered service providers
val allProviders = factory.getAllProviders()
allProviders.forEach { (cls, provider) ->
println("${cls.simpleName}: ${provider.type}")
println(" instances created: ${provider.instances.size}")
println(" dependencies: ${provider.definition.producer.params.map { it.name }}")
}
// Check if a specific service has been instantiated
val isCreated = factory.isProviderCreated(MyService::class) Tracking overrides
When a module or blueprint extension overrides a service, the override chain is preserved. Walk the overwrites
chain to see the full history:
val debugInfo = kontainer.tools.getDebugInfo()
debugInfo.services.forEach { service ->
var def = service.definition
var depth = 0
while (def.overwrites != null) {
depth++
def = def.overwrites!!
println("${service.cls.fqn} override #${depth}: was ${def.creates.fqn}")
}
} Error handling
Kontainer throws specific exceptions for different problems:
| Exception | When |
|---|---|
KontainerInconsistent | Dependency graph has problems (missing deps, configuration errors) |
ServiceNotFound | Requested service is not registered |
ServiceAmbiguous | Multiple services match the requested type |
InvalidClassProvided | Tried to register an interface or abstract class directly |
InvalidServiceFactory | Factory method is invalid |