forge_ops_tracker 0.1.0 copy "forge_ops_tracker: ^0.1.0" to clipboard
forge_ops_tracker: ^0.1.0 copied to clipboard

Dart error reporting client for a private, self-hosted ForgeOps tracker instance. Works in any Dart VM environment (server apps, CLI tools, Flutter mobile/desktop) -- see README.md for why Flutter Web [...]

forge_ops_tracker (Dart) #

Dart error reporting client for a private, self-hosted ForgeOps tracker instance. Requires Dart 3.0+. A from-scratch port of gems/forge_ops_tracker (the Rails client) -- see that gem's README for the shared design rationale; this document only covers what's Dart-specific.

This directory holds two packages:

  • forge_ops_tracker (this directory) -- the core client. Works in any Dart VM environment: server apps, CLI tools, and Flutter apps on mobile/desktop.
  • flutter_forge_ops_tracker -- a separate, small package adding Flutter-specific automatic capture (FlutterError.onError, PlatformDispatcher.onError) on top of the core client. Kept separate specifically so a plain Dart (non-Flutter) consumer of the core package never has to pull in the Flutter SDK -- the same reasoning behind Go's Gin integration living in its own nested module elsewhere in this repo.

Installation #

Not yet published to pub.flutter-io.cn -- add it as a local path dependency:

# pubspec.yaml
dependencies:
  forge_ops_tracker:
    path: /path/to/forge_ops/sdks/dart
  # Flutter apps only:
  flutter_forge_ops_tracker:
    path: /path/to/forge_ops/sdks/dart/flutter_forge_ops_tracker

Dependencies #

The core package has zero runtime dependencies: dart:io's HttpClient and dart:convert's jsonEncode cover HTTP delivery and JSON encoding, the same "reach for the language's own standard library first" choice every other client in this repo makes where its language's stdlib actually has the needed piece.

One real platform limitation this implies: dart:io is not available on Flutter Web. This client covers server/CLI Dart and Flutter on mobile/desktop, not Flutter Web. Extending it to Web would mean swapping dart:io's HttpClient for package:http (or dart:html's fetch-equivalent) behind a conditional import -- a real, known gap, not silently ignored, just out of scope for this round.

Configuration #

Set a DSN (from a project's settings page in ForgeOps), either via the FORGE_OPS_DSN environment variable or explicitly:

import 'package:forge_ops_tracker/forge_ops_tracker.dart' as forge_ops_tracker;

forge_ops_tracker.init((config) {
  config.dsn = 'https://<api_key>@your-forgeops-host/api/v1/events'; // or leave unset to read FORGE_OPS_DSN
  config.release = '...';
  config.environment = 'production';
});

Call init once at startup.

What gets reported automatically, and what doesn't #

Plain Dart (server apps, CLI tools): wrap your entry point in runGuarded:

void main() {
  forge_ops_tracker.runGuarded(() {
    // your app
  });
}

This is the closest Dart equivalent to Python's sys.excepthook or Go's global panic hook: it reports any error that escapes body -- a synchronous throw, or an unawaited Future's error -- then re-throws, so the same crash still happens exactly as it would without this wrapper. Verified directly (not assumed): both a synchronous throw and an unawaited Future's uncaught error crash a plain Dart program by default, with no zone at all, so runGuarded genuinely changes nothing about program behavior besides also reporting first.

Flutter apps: use the separate flutter_forge_ops_tracker package instead, which wires the two Flutter-specific error paths runGuarded doesn't cover on its own:

import 'package:flutter_forge_ops_tracker/flutter_forge_ops_tracker.dart';

void main() {
  forge_ops_tracker.init((config) {
    config.dsn = 'https://<api_key>@your-forgeops-host/api/v1/events';
  });
  installFlutterErrorHandlers();
  runApp(MyApp());
}
  • FlutterError.onError -- an error the framework itself catches while building, laying out, or painting a widget (what would otherwise render Flutter's own red "error" screen in debug mode).
  • PlatformDispatcher.onError -- anything that escapes the root zone instead: an async callback's error, an uncaught Future error, anything that never went through a widget build.

Both chain to whatever handler was already installed rather than replacing it, so installing this package changes nothing observable about how an error is presented besides also reporting it first -- the same "report, then don't change program behavior" rule every other client in this repo follows for its own automatic-capture path.

An error your own code catches and handles is different in both cases -- report it explicitly, right at the catch site:

try {
  chargeCard(order);
} catch (error, stackTrace) {
  forge_ops_tracker.captureException(error, stackTrace, {'order_id': order.id});
}

Delivery happens on an async drain loop with a bounded queue and a short per-request HTTP timeout (Configuration.timeout, 2s default) -- see DeliveryQueue's own comment for why Dart's single-threaded, cooperative concurrency model makes this safe without any locking, unlike the thread-based delivery queues in most of this repo's other clients. Every failure mode -- network errors, timeouts, a full queue, a malformed DSN -- is caught and dropped rather than thrown, so a broken or unreachable tracker can never take down the host app.

One real limitation worth knowing: delivery is in-memory only, with no durable on-disk queue the way the Objective-C/Swift crash reporters in this repo have. A crash reported via runGuarded or the Flutter handlers may still terminate the isolate before that delivery finishes. A caught-and-handled captureException call, made while the program is still healthy, doesn't have this limitation.

in_app backtrace frames #

A Dart StackTrace has no structured frame API at all -- the only thing available is StackTrace.toString(), a multi-line format like:

#0      ChargeService.chargeCard (package:my_app/charge_service.dart:42:7)
#1      main (package:my_app/main.dart:10:3)

so this client parses that format with a regex, the same situation the Ruby/PHP/Node clients are in for their own languages (verified directly against real caught-and-rethrown stack traces before relying on this shape). Set Configuration.packageName (e.g. "my_app", matching your pubspec.yaml's own name:) to mark a frame in_app when its location is "package:<packageName>/..."; unset by default, meaning every frame reports as not-in-app until you set it, the safe default. A dart:... location (the Dart SDK itself) and any other package:... location (a third-party pub dependency) are never in_app, regardless of configuration.

PII scrubbing #

Same behavior as every other client in this repo: the message, backtrace, and any context you attach are scanned for likely personal data -- email addresses, formatted SSNs/credit cards, known API key/token formats, and anything under a suspiciously-named key (password, api_key, ssn, and similar) -- and redacted before the payload ever leaves this process. ForgeOps itself scrubs again on arrival regardless, so this is a second, earlier layer, not the only one.

To disable it:

forge_ops_tracker.init((config) => config.scrubPii = false);

Running the tests #

cd sdks/dart
dart pub get
dart test
dart analyze

cd flutter_forge_ops_tracker
flutter pub get
flutter test
flutter analyze
0
likes
140
points
74
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Dart error reporting client for a private, self-hosted ForgeOps tracker instance. Works in any Dart VM environment (server apps, CLI tools, Flutter mobile/desktop) -- see README.md for why Flutter Web specifically isn't covered, and for the separate flutter_forge_ops_tracker package that adds Flutter-specific automatic capture on top of this one.

Homepage

Topics

#error-reporting #logging #monitoring #observability

License

MIT (license)

More

Packages that depend on forge_ops_tracker