Introduction
CodeScout captures what your Flutter app is doing and shows it to you.
Add the package and call init(). From then on every log you write is printed to your console
and kept on the device. Add one more package if you use Dio or http, and every network request
joins the same stream. Point it at a dashboard you run yourself when you want one, and you can
search all of it, replay a whole session, or watch a phone live while somebody reproduces a bug
in front of you.
CodeScout is not a crash reporter. Crashlytics tells you the app crashed. CodeScout shows you what it was doing for the five minutes before. Most teams run both.
If you would rather see it than read about it, take the tour: every screen, what it is for, and the question it answers, in one scroll.
You do not need a server to get value from it
Section titled “You do not need a server to get value from it”There is no signup, no account and no trial. This is a complete setup:
class _HomeState extends State<Home> { @override void initState() { super.initState();
// After the first frame, from a widget inside your MaterialApp. That is // where the floating button finds a widget tree to insert itself into. WidgetsBinding.instance.addPostFrameCallback((_) async { await CodeScout.instance.init( // How the panel finds a live context again after your app has // navigated somewhere else. freshContextFetcher: () => context, configuration: CodeScoutConfiguration( logging: LoggingBehavior(minimumLevel: LogLevel.all), ), ); }); }
@override Widget build(BuildContext context) => const Scaffold(body: HomeBody());}That gives you colour coded console output and a panel you can open inside your own app to read the current launch. Nothing leaves the phone, and there is nothing to configure.
Two things in that snippet are worth knowing now rather than at 2am.
init() runs from a widget under your MaterialApp, not from main(). The floating button is
inserted into the nearest Overlay, and the only one in a normal app is the one MaterialApp
builds, so a context taken from above it has nothing to insert into.
minimumLevel defaults to LogLevel.info, so .d() and .v() are dropped before they reach
even your console. LogLevel.all while you are developing is what most people want, and it is
the single most common reason someone thinks the SDK is not working.
What the in-app panel gives you
Section titled “What the in-app panel gives you”Tap the floating button and you will see three tabs.
Logs shows everything the app has written during this launch. You can search it, switch individual levels on and off, filter by tag, and pause the list when it is arriving faster than you can read it.
Network shows one row for each HTTP call, giving you its status, how long it took, and everything the call sent and received.
Errors shows the errors and fatals on their own and counts them, so that one failure happening fifty times appears as a single line instead of filling the screen.
There are two more screens behind icons in the panel’s header. Data lets you read your app’s
own SQLite tables, shared_preferences entries and Hive boxes directly on the phone, without
being able to change any of them. Its icon appears once your app has named a store with
registerDatabase, so you will not see it before then. Info answers the question you are most likely to have
first, which is whether any of this is actually working. It runs a connection test and tells you
which step failed instead of leaving you to guess.
All of that works with no server configured at all.
The two halves
Section titled “The two halves”CodeScout is two pieces you install separately.
The SDK goes in your Flutter app. code_scout does
the logging. Network capture lives in a companion package so the core stays free of HTTP
dependencies, so add code_scout_dio,
code_scout_http or
code_scout_talker to match what your app already
uses.
The dashboard is a Go binary and a Postgres, on your own infrastructure.
The SDK talks to three routes on your server and nothing else: one to check its credentials when the app starts, one to upload batches of logs, and one WebSocket for a live session. That last one is worth remembering if you put a reverse proxy in front, because a default nginx config will break it silently.
Logs are written to SQLite on the device first, so a flaky network costs you a delay rather than
your data. A background worker uploads them on whatever interval you set in LogSyncBehavior. There is no
default, so leaving that block out means nothing is ever uploaded, and that is the most common
reason a dashboard stays empty. If your server is
down they stay on the phone and are retried, and after five failures in a row the SDK waits five
minutes before trying again. It pauses, it never gives up for the rest of the launch.
Self-hosted, always
Section titled “Self-hosted, always”There is no hosted offering today, and no telemetry. Your logs go to a server you run and nowhere else. That is not a promise in a privacy policy, it is the only address the SDK is ever given.
The dashboard makes one outbound request of its own: once a day it asks GitHub whether there is a newer release. It sends nothing about your instance, and you can turn it off in Settings.
Before you ship it
Section titled “Before you ship it”The floating button is not automatically debug only. init() creates it in release builds too,
so call CodeScout.instance.hideIcon() or guard it with kDebugMode if you do not want your
users finding it.
Console printing is different: that already defaults to off in release builds.
Where to go next
Section titled “Where to go next”If you want to see the whole product before committing to anything, take the tour.
If you are starting out, Getting started has you logging in about two minutes.
Once logs are arriving, Errors and grouping is the screen most people end up using every day.
If you are evaluating this for production, read Redaction and privacy and Controlling volume first. Those are the two decisions worth making deliberately before you point it at real users.
If you want the thing that makes it different from a log aggregator, that is Watching a device live.
And if you debug with an AI agent, connect it to your logs so it can read what your app actually did rather than what you remember it doing.