Dartvel, home
Docs
Features
Studio
Cloud
Compared

Search the site

GitHub
pub.dev

GETTING STARTED

Getting started
Existing Flutter apps
Existing Native apps
Run on your phone

APP

UI and styling
Routing
State
Accessibility
Keyboard shortcuts
Localization
Devices and desktop
Native device access
Media, 3D and XR

DATA

Data models
Forms
Search
Sync and offline
Import and export
Change capture
Database
Cache
File storage
Images
Privacy and erasure

BACKEND

Backend functions
Auth and sessions
Authorization
Queues and jobs
Workers and memory
Notifications and mail
Outbound HTTP
AI
Webhooks
GraphQL and OpenAPI
API keys and OAuth
Multi-tenancy
Billing and commerce
Modules

OPERATIONS

Edge security
Secrets and environments
Monitoring
Releases

SHIPPING

Build targets
Telegram Mini Apps
Static web hosting
Servers and deploying

REFERENCE

Testing
CLI reference
Coding agents

OPERATIONS

Monitoring

Every generated backend serves metrics and a health check, and every app records its own crashes, with nothing to install.

Alerts, incidents and consent-aware analytics build on the same signals.

ON THIS PAGE

Read metrics and health from any backend

Log a line, on the client and on the server

Follow one request across services

Crash reports are on from the first build

Alert on error budgets and open incidents

Product analytics that respect consent

Status

Read metrics and health from any backend

curl https://your-host/metrics
curl https://your-host/health
dartvel metrics

Copy code to clipboard

GET /metrics serves Prometheus text, so any Prometheus-compatible scraper can read it.

GET /health runs real checks with a deadline and answers with each result.

dartvel metrics fetches the running server's /metrics and says plainly when no server answers.

// Registered on first use, and served at GET /metrics as Prometheus text.
DV.ObservabilityAndLogging.metrics
    .counter('refunds_total', <String, String>{'currency': 'usd'})
    .increment();

DV.ObservabilityAndLogging.metrics.gauge('queue_depth').set(12);

DV.ObservabilityAndLogging.metrics
    .histogram('checkout_seconds')
    .observe(0.42);

Copy code to clipboard

// Each check answers for itself when GET /health is asked, under a
// deadline. Degraded is not down: a cold cache is slow, and reporting it
// as an outage pages somebody for nothing.
DV.ObservabilityAndLogging.health.register('payments', () async {
  final bool reachable = await gatewayReachable();
  return reachable
      ? DVHealthResult.up()
      : DVHealthResult.down('the gateway did not answer');
});

Copy code to clipboard

Log a line, on the client and on the server

DV.log writes one line with whatever belongs beside it, and DV.ObservabilityAndLogging is the rest of the surface. There is no second logger to configure per package.

The two halves take different arguments. In the app, level is a string and defaults to info. On the server it is a DVLogLevel, and a line can carry a code.

A code is what an alert rule matches on, so it survives a rewording of the message.

// One line, with whatever belongs beside it. DV.log and
// DV.ObservabilityAndLogging are the surface; there is no second logger to
// configure per package.
void recordRefund(String orderId, int cents) {
  DV.log(
    'refunded an order',
    context: <String, Object>{'order': orderId, 'cents': cents},
  );
}

Copy code to clipboard

// The same two names on the server: DV.log for a line, and
// DV.ObservabilityAndLogging for everything the server serves about itself.
DV.log('Refund accepted', context: <String, Object?>{'orderId': 'o-42'});

DV.log(
  'Gateway slow',
  level: DVLogLevel.warn,
  // A code is what an alert rule matches on. Wording changes; this does not.
  code: 'PAY-SLOW',
  context: <String, Object?>{'gateway': 'stripe', 'ms': 2400},
);

Copy code to clipboard

Follow one request across services

Each request joins the W3C traceparent it arrives with, or starts one, and sends it back on the response.

Sampling is decided from the trace id, so every service in a trace makes the same choice without talking to the others.

Recent spans are kept in memory and served at /_dartvel/traces when diagnostics endpoints are on.

// Timed as one named step, so a slow checkout points at the work inside
// it instead of at the request as a whole.
Future<T> priced<T>(Future<T> Function() work) =>
    DV.ObservabilityAndLogging.trace<T>('pricing', work);

Copy code to clipboard

// The request already has a span, joined to the traceparent it arrived
// with. This is for the work inside it worth seeing on its own.
final DVSpan span =
    DV.ObservabilityAndLogging.tracer.startSpan('reprice-basket');
span.setAttribute('basket.id', 'b-9');
try {
  await Future<void>.delayed(const Duration(milliseconds: 5));
} finally {
  // In a finally: a thrown error would otherwise leave the span open, and
  // the trace then shows a request that never ended.
  span.end();
}

Copy code to clipboard

Partial

Spec section: Distributed Tracing

Planned work and implementation limits

No OTLP exporter, so spans do not reach a collector yet.

Only the request itself gets a span. Database queries, outbound HTTP, jobs and AI calls do not.

A job is not linked back to the request that queued it.

Crash reports are on from the first build

The generated client installs crash reporting at startup. A crash is written as it happens and sent on the next launch, once.

DV.Crashes.record reports an error you caught. On the web, uncaught errors and rejected promises are recorded too.

With sink: dartvel under dartvel.crashes, your own backend receives reports. Server processes record a 500, a failing schedule and a dead-lettered job.

Partial

Spec section: Crash Reporting and Release Health

Planned work and implementation limits

No native crash handlers for Android, iOS or Windows, so a crash below Dart is not caught.

Nothing reads stored reports back yet: no grouping, no Studio view and no Sentry or Crashlytics sink.

No symbol upload or source maps, so stack traces are not symbolicated.

Alert on error budgets and open incidents

DVServiceLevel sets a success-rate objective and reports how fast the error budget is burning.

DVAlerting fires a rule after it has held for a set time and delivers through DV.Notifications or PagerDuty, once per episode.

An alert opens an incident with a timeline, and a status snapshot shows each component's health without internal detail.

// A promise with a budget attached, and a rule that fires on the budget
// instead of on a single bad minute.
const DVServiceLevel checkout = DVServiceLevel(
  name: 'checkout',
  objective: DVObjective.successRate(0.995, over: Duration(days: 30)),
  applies: DVAppliesTo.backendFunction('placeOrder'),
);

final DVAlertRule backlog = DVAlertRule(
  name: 'mail-backlog',
  signal: const DVSignalRef.queueDepth('mail'),
  condition: const DVAlertWhen.above(100),
  // A rule that fires on the first sample pages somebody for a spike that
  // cleared itself.
  forDuration: const Duration(minutes: 5),
  notify: const <DVAlertTarget>[DVAlertTarget.team('ops')],
);

Copy code to clipboard

Partial

Spec section: Alerting, SLOs and Status Pages

Planned work and implementation limits

No hosted status page yet, and no incidents view in Studio.

Rule state and incident history live in memory and are lost on restart.

PagerDuty is the only pager, and there is no latency objective.

Product analytics that respect consent

Declare consent categories under dartvel.analytics. Events in a denied category are dropped at the call, never sent later.

Events are typed classes. One that names a sensitive model field is refused.

The generated app shows a consent banner and a settings page, and asks again when your policy version changes.

Partial

Spec section: Product Analytics and Consent

Planned work and implementation limits

On the web, consent is held in memory, so the banner asks again on each visit.

Events from the app stay on the device. No backend endpoint receives them yet.

No PostHog or Mixpanel adapter, and the banner is English only.

Status

Partial

Spec section: Monitoring and Observability

Planned work and implementation limits

Only the web process writes logs anywhere. It writes JSON lines to stdout, at the level DARTVEL_LOG_LEVEL sets.

Worker and cron processes have no sink, so what they log stays in a buffer in the process.

The Flutter app prints to the debug console and nothing more. Its logs never leave the device.

dartvel logs and dartvel traces say so when they have nothing to read.

PREVIOUS Secrets and environments Keys that stay on the server, checked at build and deploy
NEXT Releases Flags, branch previews, rollouts and old clients
GitHub
pub.dev
npm
Acknowledgements
Privacy
Terms

FSL-1.1-MIT licensed. Built with Dartvel.

Dartvel is made by

SigmaDev Digital

To the bottom