Biome 26 🧹 Debugging Configuration — Tracing and LSP logs
The Biome configuration is a declarative file, but the tool’s behavior depends on where the file is found, which overrides apply, and which version of the binary the editor is actually running. When the formatting does not apply, the rule does not fire, or the CLI and the editor disagree, the diagnosis is not the guess—it is the evidence. Biome provides the two evidence sources: the CLI tracing log, which records the timing and the internal span of the command, and the daemon log, which records the language server’s activity. The tracing is the mechanism that the performance problem is the measured, and the daemon log is the mechanism that the editor integration is the verified.
The tracing log is the CLI feature since Biome 2.0, and the combination of the --log-level=tracing, the --log-kind=json, and the --log-file=<path> is the mechanism that the full timing data is the captured . The output is the large JSON file, and the jq is the mechanism that the specific span is the filtered. The span names—the update_module_graph_internal, the pull_diagnostics, the format_file, and the open_file_internal—are the mechanisms that the specific phase is the inspected . The daemon log is the separate file, and the biome-logs directory is the mechanism that the LSP activity is the recorded.
Key point: The CLI tracing is the --log-level=tracing --log-kind=json --log-file=tracing.json combination. The jq filters the specific span by the name. The daemon log is in the biome-logs directory inside the Biome cache, and the BIOME_LOG_PATH is the override. The configuration resolution is the first check—the nearest biome.json from the working directory, then the upward traversal, then the home directory—and the editor’s biome.configurationPath is the override that the relative path may break since v2.4.0.
Why the tracing and the daemon log exist
The performance-blind problem. A Biome command that takes the ten seconds on the large project gives no clue about which phase is the slow. The tracing log records the span timing, and the span timing is the mechanism that the update_module_graph_internal or the pull_diagnostics is the identified . The blind is the reason the tracing exists, and the tracing is the mechanism that the performance problem is the data.
The editor-versus-CLI problem. The CLI formats the file correctly, and the editor does not. The two are the different binaries and the different configuration resolution. The daemon log records the LSP activity, and the log is the mechanism that the editor’s binary and the configuration path are the verified . The mismatch is the reason the daemon log exists, and the log is the mechanism that the integration is the debugged.
The config-resolution problem. The Biome looks for the nearest biome.json from the working directory, then the parent folders, then the home directory . The editor’s working directory is the project root, and the CLI’s is where the command is the executed. The two can be the different, and the different is the mechanism that the CLI finds the config and the editor does not—or the reverse . The resolution is the reason the config is the debugged, and the resolution is the mechanism that the biome.configurationPath is the override.
The relative-path problem. The Biome v2.4.0 introduced the regression: the biome.configurationPath with the relative path does not work in the VS Code, and the LSP server is unable to communicate with the editor . The workaround is the absolute path, and the absolute path is the mechanism that the regression is the avoided . The regression is the reason the configuration path is the debugged, and the path is the mechanism that the editor is the verified.
The plugin-shadowing problem. A nested biome.json shadows the root plugins even with the extends: "//", because the extends does not inherit the plugins . The plugin does not fire, and the reason is the configuration discovery. The root-level consolidation is the mechanism that the plugin fires everywhere, and the consolidation is the reason the plugin is the debugged .
a. The CLI tracing and the jq filtering
The CLI tracing is the three flags, and the flags are the requirement. The --log-level=tracing records the span timing, the --log-kind=json formats the output, and the --log-file=<path> writes the file.
# The tracing command
biome lint --log-level=tracing --log-kind=json --log-file=tracing.json
The --log-level=tracing is the mechanism that the span timing is the recorded . The --log-kind=json is the mechanism that the output is the JSON, and the JSON is the reason the jq can filter it. The --log-file=tracing.json is the mechanism that the log is the file rather than the stdout.
The jq filtering is the mechanism that the specific span is the extracted. The update_module_graph_internal is the module graph build, and the pull_diagnostics is the analysis.
# The slowest module graph file
cat tracing.json | jq 'select(.span.name == "update_module_graph_internal") | { path: .span.path, time_busy: .["time.busy"], time_idle: .["time.idle"] }' > filtered.json
# The slowest diagnostics file
cat tracing.json | jq '. | select(.span.name == "pull_diagnostics") | { path: .span.path, time_busy: .["time.busy"], time_idle: .["time.idle"] }' > filtered.json
The jq 'select(.span.name == "update_module_graph_internal")' is the mechanism that the specific span is the selected . The time_busy and the time_idle are the timing fields, and the path is the file path. The filtered.json is the mechanism that the result is the readable.
The format_file and the open_file_internal are the other spans. The format_file is the per-file format timing, and the open_file_internal is the file open and parse, with the reason field that tells whether the scanner, the watcher, or the client request is the cause .
# The slowest format file
cat tracing.json | jq 'select(.span.name == "format_file") | { path: .span.path, time_busy: .["time.busy"] }' > filtered.json
# The slowest open file
cat tracing.json | jq 'select(.span.name == "open_file_internal") | { path: .span.path, reason: .span.reason, time_busy: .["time.busy"] }' > filtered.json
The format_file is the mechanism that the slow file is the identified, and the open_file_internal is the mechanism that the slow parse is the identified. The reason is the mechanism that the cause is the known—the scanner is the initial scan, the watcher is the file change, and the client is the lint or the format.
b. The daemon log and the configuration resolution
The daemon log is the LSP server’s activity. The log is in the biome-logs directory inside the Biome cache, and the exact path is the biome __print_cache_dir command.
# The daemon log directory
biome __print_cache_dir
# The biome-logs directory is inside this path.
# The log directory override
BIOME_LOG_PATH="$PWD/.biome-logs" biome start
# The log prefix override
BIOME_LOG_PREFIX_NAME=my-project.log biome start
The biome __print_cache_dir is the mechanism that the cache path is the printed . The BIOME_LOG_PATH is the mechanism that the log directory is the override, and the override is the reason the project-specific log is the kept . The BIOME_LOG_PREFIX_NAME is the mechanism that the log file is the distinguished, and the distinguished is the reason the multiple projects are the separate.
The configuration resolution is the separate check. The Biome looks for the biome.json or the biome.jsonc or the .biome.json in the current working directory, then the parent folders, then the home directory . The editor’s working directory is the project root, and the CLI’s is the command’s directory.
# The configuration resolution
The current working directory → the biome.json
The parent folders → the biome.json
The home directory → the config
The default configuration → if the none found
The biome.configurationPath is the editor’s override. The relative path is the regression in the v2.4.0, and the absolute path is the workaround .
// .vscode/settings.json — the absolute path
{
"biome.configurationPath": "/Users/you/project/biome.json"
}
The biome.configurationPath with the absolute path is the mechanism that the editor’s config is the resolved . The relative path is the mechanism that the regression is the triggered, and the absolute is the reason the editor is the working.
The biome.lsp.bin is the separate override. The binary path is the mechanism that the editor’s Biome version is the pinned, and the pin is the reason the editor and the CLI are the same version .
// .vscode/settings.json — the binary override
{
"biome.lsp.bin": "/Users/you/project/node_modules/@biomejs/cli-darwin-arm64/biome"
}
The biome.lsp.bin is the mechanism that the editor’s binary is the explicit . The explicit is the reason the editor and the CLI are the consistent, and the consistency is the mechanism that the format-on-save is the same as the CLI.
The plugin shadowing is the separate check. The nested biome.json shadows the root plugins, and the extends: "//" does not inherit the plugins . The root-level consolidation is the mechanism that the plugin fires everywhere, and the consolidation is the reason the plugin is the working.
// biome.json — the root plugins
{
"plugins": ["./rules/my-rule.grit"]
}
The root plugins is the mechanism that the plugin is the registered . The nested file that omits the plugins is the mechanism that the plugin does not fire, and the non-firing is the reason the plugin is the debugged. The biome check --formatter-enabled=false --assist-enabled=false is the mechanism that the plugin output is the isolated, and the isolation is the reason the plugin is the debugged .
Complete Example Session
# ============================================
# PART 1: THE CLI TRACING
# ============================================
biome lint --log-level=tracing --log-kind=json --log-file=tracing.json
# The tracing.json contains the span timing.
# ============================================
# PART 2: THE MODULE GRAPH FILTER
# ============================================
cat tracing.json | jq 'select(.span.name == "update_module_graph_internal") | { path: .span.path, time_busy: .["time.busy"], time_idle: .["time.idle"] }' > filtered.json
# The filtered.json has the slowest module graph files.
# ============================================
# PART 3: THE DIAGNOSTICS FILTER
# ============================================
cat tracing.json | jq '. | select(.span.name == "pull_diagnostics") | { path: .span.path, time_busy: .["time.busy"], time_idle: .["time.idle"] }' > filtered.json
# The filtered.json has the slowest analysis files.
# ============================================
# PART 4: THE FORMAT FILE FILTER
# ============================================
cat tracing.json | jq 'select(.span.name == "format_file") | { path: .span.path, time_busy: .["time.busy"] }' > filtered.json
# The filtered.json has the slowest format files.
# ============================================
# PART 5: THE OPEN FILE FILTER
# ============================================
cat tracing.json | jq 'select(.span.name == "open_file_internal") | { path: .span.path, reason: .span.reason, time_busy: .["time.busy"] }' > filtered.json
# The filtered.json has the slowest parse files with the reason.
# ============================================
# PART 6: THE DAEMON LOG DIRECTORY
# ============================================
biome __print_cache_dir
# The biome-logs directory is inside the printed path.
# ============================================
# PART 7: THE LOG OVERRIDE
# ============================================
BIOME_LOG_PATH="$PWD/.biome-logs" biome start
# The log directory is the project-specific.
# ============================================
# PART 8: THE CONFIGURATION RESOLUTION
# ============================================
# The current working directory → the biome.json
# The parent folders → the biome.json
# The home directory → the config
# The default configuration → if the none found
# ============================================
# PART 9: THE CONFIGURATION PATH
# ============================================
# .vscode/settings.json — the absolute path
{
"biome.configurationPath": "/Users/you/project/biome.json"
}
# The relative path is the regression in the v2.4.0.
# ============================================
# PART 10: THE CHECKLIST
# ============================================
# [ ] The tracing flags are the three.
# [ ] The jq filters the specific span.
# [ ] The daemon log is in the biome-logs.
# [ ] The BIOME_LOG_PATH is the override.
# [ ] The configuration resolution is the first check.
# [ ] The configurationPath is the absolute path.
The ten parts covered the CLI tracing, the module graph filter, the diagnostics filter, the format file filter, the open file filter, the daemon log directory, the log override, the configuration resolution, the configuration path, and the checklist. The through-line is that the tracing is the performance evidence, and the configuration resolution is the first check.
Quick Reference
The tracing flags
| Flag | Purpose |
|---|---|
--log-level=tracing | The span timing |
--log-kind=json | The JSON output |
--log-file=<path> | The file output |
The span names
| Span | Purpose |
|---|---|
update_module_graph_internal | The module graph build |
pull_diagnostics | The analysis |
format_file | The per-file format |
open_file_internal | The open and parse, with the reason |
The daemon log
| Aspect | Detail |
|---|---|
| The directory | The biome-logs inside the cache |
| The cache path | The biome __print_cache_dir |
| The override | The BIOME_LOG_PATH |
| The prefix | The BIOME_LOG_PREFIX_NAME |
The configuration resolution
| Order | Location |
|---|---|
| 1 | The current working directory |
| 2 | The parent folders |
| 3 | The home directory |
| 4 | The default configuration |
Best Practices
✅ Do This:
# Use the three tracing flags
biome lint --log-level=tracing --log-kind=json --log-file=tracing.json // ✅
# Use the jq to filter the span
cat tracing.json | jq 'select(.span.name == "pull_diagnostics")' > filtered.json // ✅
# Use the absolute path for the configurationPath
"biome.configurationPath": "/Users/you/project/biome.json" // ✅
# Use the BIOME_LOG_PATH for the project-specific log
BIOME_LOG_PATH="$PWD/.biome-logs" biome start // ✅
❌ Don’t Do This:
# Assume the tracing is the default
biome lint // the span timing is not the recorded // ❌
# Use the relative path for the configurationPath
"biome.configurationPath": "./biome.json" // the v2.4.0 regression // ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| The tracing is the huge | The file is the large | Use the jq to filter |
| The daemon log is the missing | The log directory is the different | Use the biome __print_cache_dir |
| The editor ignores the config | The relative path is the regression | Use the absolute path |
| The plugin does not fire | The nested config shadows the root | Consolidate the root plugins |
Real-World Examples
1. The tracing command
biome lint --log-level=tracing --log-kind=json --log-file=tracing.json
2. The module graph filter
cat tracing.json | jq 'select(.span.name == "update_module_graph_internal")'
3. The diagnostics filter
cat tracing.json | jq 'select(.span.name == "pull_diagnostics")'
4. The format file filter
cat tracing.json | jq 'select(.span.name == "format_file")'
5. The open file filter
cat tracing.json | jq 'select(.span.name == "open_file_internal")'
6. The daemon log directory
biome __print_cache_dir
7. The log override
BIOME_LOG_PATH="$PWD/.biome-logs" biome start
8. The absolute configuration path
{ "biome.configurationPath": "/Users/you/project/biome.json" }
9. The binary override
{ "biome.lsp.bin": "/Users/you/project/node_modules/@biomejs/cli-darwin-arm64/biome" }
10. The plugin isolation
biome check --formatter-enabled=false --assist-enabled=false file.ts
Visual
The Tracing Flow
┌──────────────────────────────────────────────────────────────┐
│ THE TRACING FLOW │
│ │
│ The Biome command │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ --log-level=tracing │ The span timing │
│ │ --log-kind=json │ The JSON │
│ │ --log-file=path │ The file │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ tracing.json │ The large file │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ jq 'select(...)' │ The filtered span │
│ └─────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
The Configuration Resolution
┌──────────────────────────────────────────────────────────────┐
│ THE CONFIGURATION RESOLUTION │
│ │
│ The current working directory │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ The biome.json │ The first found │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ The parent folders │ The upward traversal │
│ └──────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ The home directory │ The last fallback │
│ └─────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
The Editor Override
┌──────────────────────────────────────────────────────────────┐
│ THE EDITOR OVERRIDE │
│ │
│ ✅ The absolute path: │
│ ┌─────────────────────┐ │
│ │ configurationPath: │ The resolved │
│ │ /Users/you/... │ │
│ └─────────────────────┘ │
│ │
│ ❌ The relative path: │
│ ┌─────────────────────┐ │
│ │ configurationPath: │ The v2.4.0 regression │
│ │ ./biome.json │ The LSP fails │
│ └─────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| The tracing flags | The --log-level=tracing, the --log-kind=json, the --log-file |
| The span names | The update_module_graph_internal, the pull_diagnostics, the format_file, the open_file_internal |
| The daemon log | The biome-logs inside the cache |
| The log override | The BIOME_LOG_PATH |
| The configuration resolution | The cwd → the parent → the home → the default |
| The editor path | The absolute biome.configurationPath |
| The plugin shadowing | The nested config shadows the root |
Key takeaways:
- The CLI tracing is the performance evidence. The
--log-level=tracing --log-kind=json --log-file=<path>combination records the span timing, and thejqfilters the specific span. Theupdate_module_graph_internal, thepull_diagnostics, theformat_file, and theopen_file_internalare the spans that the performance problem is the identified . - The daemon log is the editor evidence. The log is in the
biome-logsdirectory inside the Biome cache, and thebiome __print_cache_dirprints the exact path. TheBIOME_LOG_PATHand theBIOME_LOG_PREFIX_NAMEare the overrides that the project-specific log is the kept . - The configuration resolution is the first check. The Biome looks for the nearest
biome.jsonfrom the working directory, then the parent folders, then the home directory . The CLI and the editor can have the different working directories, and the different is the reason the config is the debugged. - The
biome.configurationPathwith the relative path is the v2.4.0 regression. The absolute path is the workaround, and the workaround is the reason the editor is the working . Thebiome.lsp.binis the separate binary override. - The nested
biome.jsonshadows the root plugins. Theextends: "//"does not inherit the plugins, and the plugin does not fire. The root-level consolidation is the mechanism that the plugin fires everywhere, and the--formatter-enabled=false --assist-enabled=falseis the isolation that the plugin is the debugged .
Remember: The Biome debugging is the tracing and the configuration resolution. The tracing is the CLI performance evidence, and the daemon log is the editor evidence. The configuration resolution is the first check, and the editor’s absolute path is the workaround. The nested config shadows the root, and the root-level consolidation is the fix.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!