A lightweight, open-source logging and network inspection SDK for Flutter.
Website · pub.dev · GitHub · Server
Capture logs and network calls locally, then sync them to a self-hosted Code Scout dashboard for browsing, filtering, and real-time monitoring.
- Structured logging with levels (debug, info, warning, error, fatal), tags, and metadata
- Network interception for Dio and
http— correlates request, response, and error by request ID - Local persistence in SQLite so logs survive app restarts
- Automatic batch sync — compresses logs to tar.gz and uploads on a configurable interval
- Zero third-party HTTP deps — all server communication uses
dart:io - Floating overlay for quick access to connection controls during development
- Lightweight — designed to add minimal overhead to your app
| Package | Description | pub.dev |
|---|---|---|
| code_scout | Core logging SDK | |
| code_scout_dio | Dio interceptor | |
| code_scout_http | HTTP client wrapper |
Install the core package:
flutter pub add code_scoutFor network interception, add the companion package for your HTTP client:
# For Dio users
flutter pub add code_scout_dio
# For http users
flutter pub add code_scout_httpCall init() early in your app (e.g. after the first frame):
import 'package:code_scout/code_scout.dart';
await CodeScout.instance.init(
freshContextFetcher: () => context,
configuration: CodeScoutConfiguration(
logging: LoggingBehavior(minimumLevel: LogLevel.all),
projectCredentials: ProjectCredentials(
link: 'http://your-server:24275/',
projectID: 'your-project-id',
projectSecret: 'your-project-secret',
),
sync: LogSyncBehavior(
syncInterval: Duration(seconds: 30),
maxBatchSize: 100,
),
),
);Use the shorthand methods for quick logging:
final scout = CodeScout.instance;
scout.d('Fetching user profile'); // debug
scout.i('User signed in', tags: {'auth'}); // info
scout.w('Cache miss', metadata: {'key': 'user_prefs'}); // warning
scout.e('Payment failed', error: e, stackTrace: st); // error
scout.f('Unrecoverable state'); // fatal
scout.v('Detailed trace data'); // verboseYou can also use the full form when you need to specify the level dynamically:
CodeScout.instance.log(
level: LogLevel.info,
message: 'User signed in',
tags: {'auth'},
metadata: {'userId': '123'},
);
// Awaitable (if you need to confirm persistence)
await CodeScout.instance.logMessage(
level: LogLevel.error,
message: 'Payment failed',
error: exception,
stackTrace: stackTrace,
);Network interception is provided through separate companion packages so the core SDK stays dependency-free. Each package is a one-liner to set up.
Add code_scout_dio to your dependencies, then attach the interceptor:
import 'package:code_scout_dio/code_scout_dio.dart';
final dio = Dio();
dio.interceptors.add(CodeScoutDioInterceptor());Every request, response, and error flowing through this Dio instance will be automatically captured.
Add code_scout_http to your dependencies, then wrap your client:
import 'package:code_scout_http/code_scout_http.dart';
import 'package:http/http.dart' as http;
final client = CodeScoutHttpClient(client: http.Client());
// Use it like a normal http.Client
final response = await client.get(Uri.parse('https://api.example.com/data'));CodeScoutHttpClient extends http.BaseClient, so it's a drop-in replacement anywhere you use http.Client.
Both interceptors call NetworkManager.i.processNetworkRequest/Response/Error() under the hood. Each network call gets a unique requestId that correlates the request, response, and error phases together, giving you a complete picture of every API call.
CodeScout.instance.showIcon(); // Show floating button
CodeScout.instance.hideIcon(); // Hide it
CodeScout.instance.toggleIcon(); // Toggleawait CodeScout.instance.dispose();Flutter App Code Scout Server
┌─────────────────────────┐ ┌─────────────────────────┐
│ CodeScout.log() │ │ │
│ NetworkManager │ │ POST /api/logs/dump │
│ | │ │ (multipart tar.gz) │
│ LogPersistenceService │ periodic │ | │
│ (SQLite) │──sync───────> │ Log ingestion │
│ | │ tar.gz │ | │
│ LogSyncWorker │ X-Project-ID │ MySQL storage │
│ LogCompressor (isolate) │ │ | │
└─────────────────────────┘ │ Web Dashboard │
└─────────────────────────┘
- Logs are written to a local SQLite database
- A periodic timer picks up unsync'd logs, marks them as syncing, compresses them in a background isolate, and uploads via
dart:io - On success, logs are deleted locally. On failure, they're rolled back and retried next cycle
- After 5 consecutive failures the sync worker stops automatically to avoid battery drain
| Option | Default | Description |
|---|---|---|
LoggingBehavior.minimumLevel |
LogLevel.info |
Minimum level to capture |
LoggingBehavior.enabledTags |
{'*'} |
Tags to capture (* = all) |
LoggingBehavior.printToConsole |
true in debug |
Print logs to console |
LoggingBehavior.includeCurrentStackTrace |
false |
Attach stack trace to every log |
LogSyncBehavior.syncInterval |
5 minutes | How often to sync |
LogSyncBehavior.maxBatchSize |
100 | Max logs per upload |
Code Scout needs a self-hosted server to receive logs. See the code-scout repo for setup instructions.
# Create a project (returns project_id and secret)
curl -X POST http://localhost:24275/api/project \
-H "Content-Type: application/json" \
-d '{"name": "My App", "description": "Production logs"}'Use the returned project_id and secret_key in your ProjectCredentials.
Contributions are welcome! This is a free and open-source project.
- Fork the repo
- Create a feature branch (
git checkout -b feature/my-feature) - Make your changes and run
flutter analyzeto ensure there are no issues - Submit a pull request
This project is open source. See the LICENSE file for details.