voo_telemetry 0.2.3
voo_telemetry: ^0.2.3 copied to clipboard
OpenTelemetry integration for VooFlutter with support for traces, metrics, and logs.
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:voo_telemetry/voo_telemetry.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize VooTelemetry with your DevStack API endpoint
await VooTelemetry.initialize(
endpoint: 'http://localhost:5000', // Replace with your DevStack API URL
apiKey: 'your-api-key', // Optional: Add your API key
serviceName: 'voo-example-app',
serviceVersion: '2.0.0',
additionalAttributes: {'deployment.environment': 'development', 'app.platform': 'flutter'},
debug: true,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
title: 'VooTelemetry Demo',
theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true),
home: const TelemetryDemoPage(),
);
}
class TelemetryDemoPage extends StatefulWidget {
const TelemetryDemoPage({super.key});
@override
State<TelemetryDemoPage> createState() => _TelemetryDemoPageState();
}
class _TelemetryDemoPageState extends State<TelemetryDemoPage> {
final _tracer = VooTelemetry.instance.getTracer('demo-app');
final _meter = VooTelemetry.instance.getMeter('demo-metrics');
final _logger = VooTelemetry.instance.getLogger('demo-logger');
late final Counter _buttonClickCounter;
late final Histogram _operationDuration;
late final Gauge _activeUsers;
int _clickCount = 0;
String _status = 'Ready';
@override
void initState() {
super.initState();
// Initialize metrics instruments
_buttonClickCounter = _meter.createCounter('button_clicks', description: 'Number of button clicks', unit: 'clicks');
_operationDuration = _meter.createHistogram('operation_duration', description: 'Duration of operations', unit: 'ms');
_activeUsers = _meter.createGauge('active_users', description: 'Number of active users');
// Set initial gauge value
_activeUsers.set(1);
// Log app startup
_logger.info('Application started', attributes: {'screen': 'TelemetryDemoPage', 'platform': Theme.of(context).platform.toString()});
}
Future<void> _performTracedOperation() async {
setState(() => _status = 'Running traced operation...');
try {
await _tracer.withSpan('demo-operation', (span) async {
span.setAttribute('user.action', 'button_click');
span.setAttribute('click.count', _clickCount);
// Simulate some work
span.addEvent('Starting processing');
await Future<void>.delayed(const Duration(milliseconds: 500));
span.addEvent('Fetching data');
await _fetchData(span);
span.addEvent('Processing complete');
setState(() => _status = 'Operation completed successfully');
});
} catch (e, stackTrace) {
setState(() => _status = 'Operation failed: $e');
_logger.error('Operation failed', error: e, stackTrace: stackTrace);
}
}
Future<void> _fetchData(Span parentSpan) async {
await _tracer.withSpan('fetch-data', (span) async {
span.setAttribute('http.method', 'GET');
span.setAttribute('http.url', 'https://jsonplaceholder.typicode.com/posts/1');
final stopwatch = Stopwatch()..start();
try {
final dio = Dio();
final response = await dio.get('https://jsonplaceholder.typicode.com/posts/1');
span.setAttribute('http.status_code', response.statusCode);
span.setAttribute('response.size', response.data.toString().length);
_logger.info('Data fetched successfully', attributes: {'status_code': response.statusCode, 'data_size': response.data.toString().length});
} finally {
stopwatch.stop();
_operationDuration.record(stopwatch.elapsedMilliseconds.toDouble(), attributes: {'operation': 'fetch_data'});
}
}, kind: SpanKind.client);
}
void _recordMetrics() {
setState(() {
_clickCount++;
_status = 'Recording metrics...';
});
// Record counter metric
_buttonClickCounter.increment(attributes: {'button': 'record_metrics', 'screen': 'demo'});
// Record gauge metric
_activeUsers.set(_clickCount.toDouble());
// Log the action
_logger.info('Metrics recorded', attributes: {'click_count': _clickCount, 'timestamp': DateTime.now().toIso8601String()});
setState(() => _status = 'Metrics recorded: $_clickCount clicks');
}
void _simulateError() {
setState(() => _status = 'Simulating error...');
try {
// This will throw an error
throw Exception('Simulated error for demonstration');
} catch (e, stackTrace) {
// Record the exception
VooTelemetry.instance.recordException(e, stackTrace, attributes: {'error.simulated': true, 'user.action': 'simulate_error_button'});
setState(() => _status = 'Error recorded and sent');
}
}
Future<void> _flushTelemetry() async {
setState(() => _status = 'Flushing telemetry data...');
await VooTelemetry.instance.flush();
setState(() => _status = 'Telemetry data flushed');
_logger.info('Manual flush completed', attributes: {'click_count': _clickCount});
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text('VooTelemetry Demo')),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('OpenTelemetry Integration Demo', style: Theme.of(context).textTheme.headlineMedium),
const SizedBox(height: 20),
Text('Status: $_status', style: Theme.of(context).textTheme.bodyLarge),
const SizedBox(height: 10),
Text('Click count: $_clickCount', style: Theme.of(context).textTheme.bodyMedium),
const SizedBox(height: 40),
Wrap(
spacing: 10,
runSpacing: 10,
alignment: WrapAlignment.center,
children: [
ElevatedButton.icon(onPressed: _performTracedOperation, icon: const Icon(Icons.play_arrow), label: const Text('Run Traced Operation')),
ElevatedButton.icon(onPressed: _recordMetrics, icon: const Icon(Icons.analytics), label: const Text('Record Metrics')),
ElevatedButton.icon(
onPressed: _simulateError,
icon: const Icon(Icons.error_outline),
label: const Text('Simulate Error'),
style: ElevatedButton.styleFrom(backgroundColor: Colors.orange),
),
ElevatedButton.icon(
onPressed: _flushTelemetry,
icon: const Icon(Icons.cloud_upload),
label: const Text('Flush Telemetry'),
style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
),
],
),
const SizedBox(height: 40),
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Text('Telemetry Types Demonstrated:', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 10),
const Text('✅ Distributed Tracing (Spans)'),
const Text('✅ Metrics (Counter, Histogram, Gauge)'),
const Text('✅ Structured Logging'),
const Text('✅ Exception Tracking'),
const Text('✅ Context Propagation'),
],
),
),
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// Navigate to settings or info page
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('VooTelemetry Info'),
content: const Text(
'This demo app showcases the VooTelemetry package '
'integration with OpenTelemetry.\n\n'
'All telemetry data is sent to your configured '
'DevStack API endpoint for processing and visualization.',
),
actions: [TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('OK'))],
),
);
},
tooltip: 'Info',
child: const Icon(Icons.info_outline),
),
);
@override
void dispose() {
_logger.info('Application closing', attributes: {'total_clicks': _clickCount});
super.dispose();
}
}