Debugging & Diagnostics
Kindlings provides three diagnostic tools: debug logging (to see what the macro generates), flame graphs (to profile compilation performance), and configurable timeouts (to prevent runaway derivation).
Debug logging
Every module supports two ways to enable debug logging: a scoped import (per-file) or a scalac option (project-wide).
Via import (per-file)
Add the module's debug import to the file where you want to inspect derivation:
// Circe
import hearth.kindlings.circederivation.debug._
// Jsoniter
import hearth.kindlings.jsoniterderivation.debug._
// Cats
import hearth.kindlings.catsderivation.debug._
The import places a LogDerivation implicit in scope. When the macro sees it, it prints the generated code, matched rules, and summoned implicits to the compiler output.
Via scalac option (project-wide)
Add a -Xmacro-settings option to your build — no code changes needed:
Module reference
| Module | Debug import | Scalac setting |
|---|---|---|
| circe-derivation | hearth.kindlings.circederivation.debug._ |
circeDerivation.logDerivation=true |
| jsoniter-derivation | hearth.kindlings.jsoniterderivation.debug._ |
jsoniterDerivation.logDerivation=true |
| cats-derivation | hearth.kindlings.catsderivation.debug._ |
catsDerivation.logDerivation=true |
| avro-derivation | hearth.kindlings.avroderivation.debug._ |
avroDerivation.logDerivation=true |
| pureconfig-derivation | hearth.kindlings.pureconfigderivation.debug._ |
pureconfigDerivation.logDerivation=true |
| tapir-schema-derivation | hearth.kindlings.tapirschemaderivation.debug._ |
tapirSchemaDerivation.logDerivation=true |
| yaml-derivation | hearth.kindlings.yamlderivation.debug._ |
yamlDerivation.logDerivation=true |
| scalacheck-derivation | hearth.kindlings.scalacheckderivation.debug._ |
scalacheckDerivation.logDerivation=true |
| sconfig-derivation | hearth.kindlings.sconfigderivation.debug._ |
sconfigDerivation.logDerivation=true |
| ubjson-derivation | hearth.kindlings.ubjsonderivation.debug._ |
ubjsonDerivation.logDerivation=true |
| xml-derivation | hearth.kindlings.xmlderivation.debug._ |
xmlDerivation.logDerivation=true |
| fast-show-pretty | hearth.kindlings.fastshowpretty.debug._ |
fastShowPretty.logDerivation=true |
Generation logging for the non-derivation macro modules
optics, mock and di are not type-class derivations, but they still generate code — so they offer the same
"see the generated code and the logic that led to it" capability under the name logGeneration. Enable it the same
two ways (a scoped debug import, or a scalac option). The message shows the parsed input (path steps / overridden
members / resolution) and the pretty-printed generated code.
// optics — preview the parsed path + generated PathModify
import hearth.kindlings.optics.debug._
// mock — preview the overridden members + generated mock
import hearth.kindlings.mock.debug._
// di — preview the resolution + generated wiring (all endpoints, including DI.plan)
import hearth.kindlings.di.debug._
| Module | Debug import | Scalac setting |
|---|---|---|
| optics | hearth.kindlings.optics.debug._ |
optics.logGeneration=true |
| mock | hearth.kindlings.mock.debug._ |
mock.logGeneration=true |
| di | hearth.kindlings.di.debug._ |
di.logGeneration=true |
| di-cats | hearth.kindlings.dicats.debug._ |
diCats.logGeneration=true |
DI wiring graph (how things combine)
To see only how entities/resources are wired together for a single DI run — without the full generation log — di
(and di-cats, via diCats.logWiring) additionally offers a ZIO-Magic-style wiring graph
(ZLayer.Debug.tree / ZLayer.Debug.mermaid analog). Enable it project-wide with di.logWiring=tree (a DAG-aware
ASCII dependency tree) or di.logWiring=mermaid (a Mermaid diagram + render link):
For the DI.plan endpoint you can also request it inline with .debugTree / .debugMermaid — see the
DI guide.
Flame graph generation
Profile how long each derivation step takes using Hearth's built-in flame graph support. Add these scalac options:
// build.sbt
scalacOptions ++= Seq(
"-Xmacro-settings:hearth.mioBenchmarkScopes=true",
"-Xmacro-settings:hearth.mioBenchmarkFlameGraphDir=/tmp/kindlings-flamegraphs"
)
This generates .speedscope.json files in the specified directory — one per macro expansion. Open them at speedscope.app to visualize where compilation time is spent.
Tip
This is most useful when a specific type takes noticeably long to derive. Focus on the hot paths: rule evaluation (type comparisons), implicit search, and expression tree construction.
Derivation timeout
All modules enforce a configurable timeout to prevent runaway macro expansions. Default: 5 seconds. If a derivation takes longer, compilation fails with a clear error — preventing silent long waits.
Override per module via -Xmacro-settings when a specific type hierarchy legitimately needs more time:
// build.sbt
scalacOptions ++= Seq(
"-Xmacro-settings:circeDerivation.timeout=30s",
"-Xmacro-settings:jsoniterDerivation.timeout=1m"
)
Supported formats:
| Format | Example | Meaning |
|---|---|---|
| Plain integer | 300 |
300 seconds |
| Seconds | 300s or 300seconds |
300 seconds |
| Milliseconds | 5000ms or 5000milliseconds |
5 seconds |
| Minutes | 5m or 5minutes |
5 minutes |
Invalid values emit a compiler warning and fall back to the 120-second default.
Settings namespace per module
| Module | Settings namespace |
|---|---|
| circe-derivation | circeDerivation |
| jsoniter-derivation | jsoniterDerivation |
| cats-derivation | catsDerivation |
| avro-derivation | avroDerivation |
| pureconfig-derivation | pureconfigDerivation |
| tapir-schema-derivation | tapirSchemaDerivation |
| yaml-derivation | yamlDerivation |
| scalacheck-derivation | scalacheckDerivation |
| sconfig-derivation | sconfigDerivation |
| ubjson-derivation | ubjsonDerivation |
| xml-derivation | xmlDerivation |
| fast-show-pretty | fastShowPretty |