flutter-logging-strategy
Establish, audit, and enforce a consistent logging strategy in any Flutter/Dart project using package:logging — installs the dependency if missing, builds a…
Skill by ersanyamarya
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/ersanyamarya-flutter-logging-strategy
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: documents, security, marketing, finance
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Python package
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/flutter-logging-strategy
What's inside
- Skill name
flutter-logging-strategy- Description
- Establish, audit, and enforce a consistent logging strategy in any Flutter/Dart project using package:logging — installs the dependency if missing, builds a named-logger hierarchy with a documented level budget and redaction policy, migrates ad-hoc print/debugPrint/logger calls, finds silent catch blocks that destroy diagnostics, and audits project docs and skills so the documented strategy matches the code. Use this whenever the user mentions logging, log levels, debugPrint, print statements, diagnostics, observability, tracing, noisy logs, missing logs, "I can't tell why this failed", redacting secrets or tokens from logs, or asks to set up, clean up, standardize, or review logging — even if they never name a package or say the word "strategy".
- License
- MIT
- Instructions
- 189 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
Files (6)
- SKILL.md
- app_logger.dart.template · assets/
- evals.json · evals/
- level-budget.md · references/
- migration.md · references/
- audit_logging.py · scripts/
SKILL.md (first lines)
# Flutter logging strategy
> The Flutter app lives in `apps/<app>/`. Run `flutter`/`dart` commands from that directory; `lib/`, `test/`, and `assets/` paths below are relative to it; shell scripts live in the repo-root `scripts/`. `docs/` stays at the repo root.
Most Flutter projects don't lack logging — they lack a *strategy*. The typical state is a few dozen `debugPrint` calls with no levels, no way to turn them down, no rule about what is safe to print, and a handful of `catch (_)` blocks quietly destroying the one error message that would have explained a bug. Adding a logging package doesn't fix that. What fixes it is a level budget people can actually apply, a single sink, and an audit that finds where diagnostics are being thrown away.
This skill does four things, in order: **survey** what's there, **establish** the logger, **migrate and close holes**, then **align the docs** so the written strategy and the code agree.
Work through the phases in order. Phase 0 is not optional — the survey is what tells you whether this is a greenfield install or a cleanup, and those need different amounts of work.
## Phase 0 — Survey before you touch anything
Run the bundled audit script. It has no dependencies beyond Python 3 and reports everything the later phases need:
```bash
python3 ${CLAUDE_SKILL_DIR}/scripts/audit_logging.py <project-root> [extra-docs-root ...]
```
In this monorepo: `python3 ${CLAUDE_SKILL_DIR}/scripts/audit_logging.py apps/<app> docs .claude/skills` (run from the repo root, so the root `docs/` and the skills are also checked for stale logging claims).
It reports: whether `logging` is already a direct or transitive dependency, which other logging packages are present, every ad-hoc log site, eagerly-interpolated gated calls, silent `catch` blocks, which global error handlers are wired, whether the expensive knobs have been touched, log lines that may expose secrets, and doc/skill files whose logging claims may now be stale.
Read the report before proposing anything. Two things you learn here change the plan:
- **`logging` may already be in `pubspec.lock` as a transitive dependency.** Many common packages pull it in. If so, adding it as a direct dependency costs *zero* new packages and cannot cause version churn — worth saying out loud, because "add another dependency" is the objection you'll otherwise get.
- **The project may already use a different logger** (`logger`, `talker`, `loggy`). This skill standardizes on `package:logging`, so that means a migration — see `references/migration.md`. Tell the user the scope before you start; a logger swap touches every call site and they may reasonably want to stage it.
## Phase 1 — Install
```bash
flutter pub add logging
```
Then `flutter pub get` if the project's tooling needs it separately (`pub add` normally runs resolution itself).
Two things to check after, because both fail the build later rather than here:
- If the project's analysis options enable `sort_pub_dependencies`, the new entry must sit in alphabetical position. `pub add` normally handles this; `flutter analyze` is the check.
- If `logging` was previously transitive, confirm the output says it moved from transitive to direct rather than resolving a *new* version. A surprise version bump means something else constrained it.
## Phase 2 — One file owns the whole surface
Create a single module — `lib/core/logging/app_logger.dart` or wherever the project's shared infrastructure lives. Copy `assets/app_logger.dart.template` and adapt the logger names to the project's actual subsystems.
Three properties make this file work, and all three are worth preserving when you adapt it:
**It is the only file that imports `package:logging`.** Everything else imports *this* file and gets a ready-made logger. That keeps the level policy, the sink format, and the redaction rule in one reviewable place instead of spread across every feature.
**It is the only remaining legal `print`/`debugPrint` site, because it *is* the sink.** This sounds contradictory until you state it plainly: the ban applies to call sites, not to the implementation of the sink. Say so in the file, or the next person will "fix" it. Prefer `debugPrint` over `print` — it throttles output, so Android's logcat doesn't drop lines under bursty logging, and it satisfies the `avoid_print` lint that most strict lint sets enable.
**The sink must format `error` and `stackTrace` itself.** This is the trap worth knowing about, because the broken version looks finished: `LogRecord.toString()` renders only `[LEVEL] loggerName: message`. It does **not** include the error or the stack. So a sink written as `onRecord.listen((r) => debugPrint('$r'))` silently discards both — every call site can be passing its exception correctly and nothing ever reaches the console. If someone reports "we log errors but there's never anything useful in there", check the sink before checking the call sites.
**Named loggers form a dotted hierarchy, one per subsystem.** `logging` uses dot-separated names as its hierarchy convention. Give the project a single root prefix (its name) plus one child per genuine subsystem — networking, persistence, auth, and whatever the app's core domain is. Keep the root logger too: global error handlers log things that belong to no subsystem.
Pick subsystems by *where you'd look during an incident*, not by folder structure. Five or six is plenty. A logger per feature folder is the failure mode — it produces names nobody remembers and no filtering benefit.
### `initLogging()`
The init function sets the root level and attaches exactly one listener. Two details matter:
**Guard against double-initialization.** A second call would attach a second listener and duplicate every line thereafter. A private bool and an early return is enough — and it makes the function safe to call from tests.
**Set the level from the build mode, not a constant.** Keep `WARNING` and above in release builds. This is the choice that pays off when a user reports a bug from a real device: `adb logcat` or Console.app will still carry the warnings and errors, while the chatty levels cost one integer comparison each. Turning logging fully off in release means field reports arrive with no diagnostic content at all.
## Phase 3 — Wire the bootstrap first, literally
`initLogging()` goes at the **top** of `main()`, before every other bootstrap step.
This ordering is load-bearing, not tidiness. Bootstrap code is exactly where destructive recovery lives — cache resets, corrupt-database deletion, migration fallbacks. Those paths log the only explanation the user will ever get for missing data, and they run *early*. A logger installed after them captures nothing. Find the project's bootstrap steps that can destroy or discard data on failure, and make sure the logger is installed before them.
Then wire the two global handlers, because most projects have neither or only one:
- **`FlutterError.onError`** — synchronous framework errors. Log at `SEVERE`, then delegate to `FlutterError.presentError` so the debug red screen still appears. Skip the delegation and you've made debugging worse, not better.
- **`PlatformDispatcher.instance.onError`** — uncaught async errors. If the project already has one that swallows certain error types deliberately, *keep the swallowing* and add the logging. Check whether it binds a stack trace it then discards; that is a common and free improvement.
## Phase 4 — The level budget
This is the part that decides whether the logs stay useful. Put this table in the logger file's doc comment so it is readable at the point of use, and in the project's contributor docs.
| Level | Means | Rough budget |
|---|---|---|
| `SEVERE` | A user action failed and did **not** recover; data may be lost | Rare. Carries the error object *and* a stack. |
| `WARNING` | Degraded but recovered — will retry, fell back, or was refused | Per failed operation. Survives into release. |
| `INFO` | Coarse lifecycle, once per *logical* operation | The ceiling for "leave it on" |
| `FINE` | Per-operation detail; dev builds only | State transitions |
| `FINER` | Per-row / per-message firehose | Bulk iteration, stream traffic |
| `FINEST` | Reserve it. An unused level is a feature — it leaves headroom | — |
**The one rule to carry:** *if a line would print more than once per user action, it belongs at `FINE` or below.* This is a better test than arguing about severity, because it is checkable. Also: never log inside `build()`, and never per frame.
`WARNING` vs `SEVERE` trips people up. The question is not "how bad does this feel" but **did the operation recover?** A failed network push that stays queued for retry is `WARNING` — the system handled it. A cache reset that deleted data is `SEVERE` — nothing recovers that.
See `references/level-budget.md` for the harder calls and worked examples.
## Phase 5 — Efficiency: four rules, one of which is the whole game
`Logger.log` checks `isLoggable(level)` before doing anything else, so a call at a disabled level costs one comparison. That gives you four rules:
1. **Pass a closure for any message that interpolates.** `log.fine(() => 'resolved $n of $m')`, never `log.fine('resolved $n of $m')`. Dart evaluates arguments eagerly, so the plain form builds that string on every call *even when the level is off* — which is exactly the cost gating is supposed to avoid. `logging` invokes a function message only after `isLoggable` passes. Plain strings are fine for `warning`/`severe`: they're rare and usually enabled.
2. **Move work that exists only for the log message inside the closure.** This is the one people miss, and it's often the bigger win. If a log line needs a lookup, a count, or a `join` to build its message, that work runs unconditionally when it sits above the log call. Inside the closure it runs only when the level is on. Look for this specifically during the audit — computed-for-logging-only locals are common in code that was instrumented under pressure.
3. **Leave `recordStackTraceAtLevel` at its `Level.OFF` default.** It is a *top-level* variable in the logging package, not a static, so it's easy to set casually. Lowering it makes every record at or above that level call `StackTrace.current`; the package's own comment says "Because this is expensive, this is off by default." Pass stacks explicitly at the few sites that have one.
4. **Leave `hierarchicalLoggingEnabled` at `false` unless per-subsystem runtime control is genuinely needed.** At `false` each record publishes once to the root. At `true` a record walks the parent chain publishing at each level, and non-root loggers become individually settable. Turn it on only if the project actually wants "crank one subsystem to FINEST, leave the rest alone" — and if it does, that's a real feature worth the cost.
Pass errors and stacks as the **second and third arguments**, not folded into the message string: `log.warning('Push failed for $id', error, stackTrace)`. The sink formats them, so the output stays consistent and the error object survives as an object.
## Phase 6 — Audit the code
The script gives you findings. Your job is triage, because **not every silent path is a bug.** Present findings grouped by severity and let the user choose before editing — auto-instrumenting everything produces exactly the noise this strategy exists to prevent.
Rank findings like this:
**Highest value: errors that are caught and discarded.** `catch (_)`, or `on SomeException catch` with no variable bound, in a path a user can reach. These are worse than missing logs — the information existed and was deliberately thrown away. The pattern to look for hardest: a layer that throws a *deliberate, informative* exception, and a caller that swallows it and reports a generic failure state. The user sees "failed" and the reason exists nowhere. Fix by binding the error and logging it; leave the control flow exactly as it was.
**High value: silent drops with no signal to the caller.** A function that returns early when a precondition fails, returning `void` or a default, gives the caller nothing to report. If a user action can trigger it, that early return needs a log.
**High value: the one place a reason is readable.** Some failures are only explicable at a specific line — a status object populated after a call returns, a response code available before it's discarded. Log *there*, even if the failure is handled elsewhere.From ersanyamarya/agentry-labs, MIT.
More from ersanyamarya/agentry-labs
- 0.570score
skill-architect
ersanyamarya
Create, improve, or review Claude Code skills so they are portable, token-efficient, and spec-compliant, with scripts doing the exact work and the model doing…
SkillAFreshness: AClaude Code +9 - 0.570score
image-artifact-setup
ersanyamarya
Set up a reproducible image generator in a project (Open Graph and social cards, link previews, project or product tiles, README banners, store screenshots)…
SkillAFreshness: AClaude Code +9 - 0.570score
lighthouse-audit
ersanyamarya
Run Google Lighthouse against a URL (mobile and desktop), trace the performance, accessibility, SEO, and best-practices findings to the project's actual…
SkillAFreshness: AClaude Code +9 - 0.570score
run-fallow
ersanyamarya
Run the `fallow` CLI on any JavaScript/TypeScript project to find dead code, unused dependencies, duplication, complexity hotspots, and security candidates…
SkillAFreshness: AClaude Code +9 - 0.570score
flutter-store-assets
ersanyamarya
Operates the gentlemans-stack image tool (tools/image-gen-tools) for a Flutter app.
SkillAFreshness: AClaude Code +9 - 0.570score
humanize-and-unslop
ersanyamarya
Edit drafts into plain, specific, human-sounding writing by removing AI tells (banned vocabulary, em dashes, curly quotes, puffery, chatbot phrases, formulaic…
SkillAFreshness: AClaude Code +9 - 0.566score
flutter-bundle-google-fonts
ersanyamarya
Replaces the google_fonts package in a Flutter app with fonts bundled in the app, using the gentlemans-stack scripts/flutter/bundle-fonts.mjs to scan usages…
SkillAFreshness: AClaude Code +9 - 0.566score
flutter-brand-icons
ersanyamarya
Generate every app icon and native splash asset for a Flutter project from one single-color logo SVG, with colors taken from the project's brand doc, design…
SkillAFreshness: AClaude Code +9 - 0.565score
mongoose-schema-docs
ersanyamarya
Generate documentation and an entity-relationship diagram from a codebase's Mongoose schemas.
SkillAFreshness: AClaude Code +9 - 0.565score
sync-docs-from-diff
ersanyamarya
Update repo documentation from a git diff.
SkillAFreshness: AClaude Code +9 - 0.565score
script-writer
ersanyamarya
Generate a new shell script in scripts/ following the repo's script conventions (help header, utils.sh sourcing, signal trap, argument parsing).
SkillAFreshness: AClaude Code +9 - 0.565score
generate-commit-message
ersanyamarya
Write a Conventional Commit message in this repo's style (type(scope): Capitalized summary) from staged or unstaged changes.
SkillAFreshness: AClaude Code +9
Signals
- Stars
- 0
- Forks
- 0
- Open issues
- 0
- Last commit (this folder)
- Oct 18, 2026 (10 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/ersanyamarya/agentry-labs
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from ersanyamarya
- 0.570score
run-fallow
ersanyamarya
Run the `fallow` CLI on any JavaScript/TypeScript project to find dead code, unused dependencies, duplication, complexity hotspots, and security candidates…
SkillAFreshness: AClaude Code +9 - 0.570score
skill-architect
ersanyamarya
Create, improve, or review Claude Code skills so they are portable, token-efficient, and spec-compliant, with scripts doing the exact work and the model doing…
SkillAFreshness: AClaude Code +9 - 0.570score
flutter-store-assets
ersanyamarya
Operates the gentlemans-stack image tool (tools/image-gen-tools) for a Flutter app.
SkillAFreshness: AClaude Code +9 - 0.570score
humanize-and-unslop
ersanyamarya
Edit drafts into plain, specific, human-sounding writing by removing AI tells (banned vocabulary, em dashes, curly quotes, puffery, chatbot phrases, formulaic…
SkillAFreshness: AClaude Code +9 - 0.570score
lighthouse-audit
ersanyamarya
Run Google Lighthouse against a URL (mobile and desktop), trace the performance, accessibility, SEO, and best-practices findings to the project's actual…
SkillAFreshness: AClaude Code +9 - 0.570score
image-artifact-setup
ersanyamarya
Set up a reproducible image generator in a project (Open Graph and social cards, link previews, project or product tiles, README banners, store screenshots)…
SkillAFreshness: AClaude Code +9
Cite this page
flutter-logging-strategy skill. Agent Mart. https://agentmart.dev/skills/ersanyamarya-flutter-logging-strategy (last updated 2026-10-24)