faro 0.8.0
faro: ^0.8.0 copied to clipboard
Grafana Faro SDK for Flutter applications - Monitor your Flutter app with ease.
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:faro/faro.dart';
import 'package:http/http.dart' as http;
import 'features/user_settings/user_settings_page.dart';
import 'features/user_settings/user_settings_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
HttpOverrides.global = FaroHttpOverrides(HttpOverrides.current);
// Load user settings
final userSettingsService = UserSettingsService.instance;
await userSettingsService.init();
const faroCollectorUrl = String.fromEnvironment('FARO_COLLECTOR_URL');
final faroApiKey = faroCollectorUrl.split('/').last;
Faro().transports.add(OfflineTransport(
maxCacheDuration: const Duration(days: 3),
));
await Faro().runApp(
optionsConfiguration: FaroConfig(
appName: 'example_app',
appVersion: '2.0.1',
appEnv: 'Test',
apiKey: faroApiKey,
namespace: 'flutter_app',
anrTracking: true,
cpuUsageVitals: true,
collectorUrl: faroCollectorUrl,
enableCrashReporting: true,
memoryUsageVitals: true,
refreshRateVitals: true,
fetchVitalsInterval: const Duration(seconds: 30),
sessionAttributes: {
'team': 'mobile',
'department': 'engineering',
},
initialUser: userSettingsService.initialUser,
persistUser: userSettingsService.persistUser,
),
appRunner: () async {
runApp(DefaultAssetBundle(
bundle: FaroAssetBundle(),
child: const FaroUserInteractionWidget(child: MyApp()),
));
},
);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
setState(() {});
}
// Method to simulate an ANR by blocking the main thread
void simulateANR({int seconds = 10}) {
debugPrint(
'Simulating ANR by blocking main thread for $seconds seconds...');
final startTime = DateTime.now();
// This loop will block the main thread
while (DateTime.now().difference(startTime).inSeconds < seconds) {
// Perform intensive calculations to block the thread
for (int i = 0; i < 10000000; i++) {
final _ = i * i * i;
}
}
debugPrint('ANR simulation completed');
}
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorObservers: [FaroNavigationObserver()],
initialRoute: '/',
routes: {
'/home': (context) => const HomePage(),
'/features': (context) => const FeaturesPage(),
'/user-settings': (context) => const UserSettingsPage(),
},
home: Scaffold(
appBar: AppBar(
title: const Text('Faro Test App'),
),
body: const HomePage(),
),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
child: const Text('Change Route'),
onPressed: () {
Navigator.pushNamed(context, '/features');
},
),
],
),
);
}
}
class FeaturesPage extends StatefulWidget {
const FeaturesPage({super.key});
@override
State<FeaturesPage> createState() => _FeaturesPageState();
}
class _FeaturesPageState extends State<FeaturesPage> {
final _userSettingsService = UserSettingsService.instance;
String _currentUserDisplay = 'Not set';
@override
void initState() {
super.initState();
Faro().markEventEnd('home_event_start', 'home_page_load');
_updateCurrentUser();
}
void _updateCurrentUser() {
setState(() {
_currentUserDisplay = _userSettingsService.getCurrentUserDisplay();
});
}
void simulateANR({int seconds = 10}) {
debugPrint(
'Simulating ANR by blocking main thread for $seconds seconds...');
final startTime = DateTime.now();
while (DateTime.now().difference(startTime).inSeconds < seconds) {
for (int i = 0; i < 10000000; i++) {
final _ = i * i * i;
}
}
debugPrint('ANR simulation completed');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Features'),
),
body: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// User Settings Card
Card(
child: ListTile(
leading: const Icon(Icons.person),
title: const Text('User Settings'),
subtitle: Text('Current: $_currentUserDisplay'),
trailing: const Icon(Icons.chevron_right),
onTap: () async {
await Navigator.pushNamed(context, '/user-settings');
_updateCurrentUser();
},
),
),
const SizedBox(height: 16),
const Divider(),
const SizedBox(height: 8),
ElevatedButton(
child: const Text('HTTP POST Request - success'),
onPressed: () async {
await http.post(
Uri.parse('https://httpbin.io/post'),
body: jsonEncode(<String, String>{
'title': 'This is a title',
}),
);
},
),
ElevatedButton(
child: const Text('HTTP POST Request - fail'),
onPressed: () async {
await http.post(
Uri.parse('https://httpbin.io/unstable'),
body: jsonEncode(<String, String>{
'title': 'This is a title',
}),
);
},
),
ElevatedButton(
child: const Text('HTTP GET Request - success'),
onPressed: () async {
await http.get(Uri.parse('https://httpbin.io/get?foo=bar'));
},
),
ElevatedButton(
child: const Text('HTTP GET Request - fail'),
onPressed: () async {
await http.get(Uri.parse('https://httpbin.io/unstable'));
},
),
ElevatedButton(
child: const Text('Custom Warn Log'),
onPressed: () {
Faro().pushLog('Custom Warning Log', level: LogLevel.warn);
},
),
ElevatedButton(
child: const Text('Custom Info Log'),
onPressed: () {
Faro()
.pushLog('This is an info message', level: LogLevel.info);
},
),
ElevatedButton(
child: const Text('Custom Error Log'),
onPressed: () {
Faro().pushLog('This is an error message',
level: LogLevel.error);
},
),
ElevatedButton(
child: const Text('Custom Debug Log'),
onPressed: () {
Faro().pushLog('This is a debug message',
level: LogLevel.debug);
},
),
ElevatedButton(
child: const Text('Custom Trace Log'),
onPressed: () {
Faro().pushLog('This is a trace message',
level: LogLevel.trace);
},
),
ElevatedButton(
child: const Text('Custom Measurement'),
onPressed: () {
Faro().pushMeasurement(
{'custom_value': 1}, 'custom_measurement');
},
),
ElevatedButton(
child: const Text('Custom Event'),
onPressed: () {
Faro().pushEvent('custom_event');
},
),
ElevatedButton(
child: const Text('Error'),
onPressed: () {
setState(() {
throw Error();
});
},
),
ElevatedButton(
child: const Text('Exception'),
onPressed: () {
setState(() {
double _ = 0 / 0;
throw Exception('This is an Exception!');
});
},
),
ElevatedButton(
child: const Text('Mark Event Start'),
onPressed: () async {
Faro().markEventStart('event1', 'event1_duration');
},
),
ElevatedButton(
child: const Text('Mark Event End'),
onPressed: () async {
Faro().markEventEnd('event1', 'event1_duration');
},
),
ElevatedButton(
onPressed: () => simulateANR(),
child: const Text('Simulate ANR (10s)'),
),
ElevatedButton(
onPressed: () => simulateANR(seconds: 8),
child: const Text('Simulate ANR (8s)'),
),
ElevatedButton(
child: Text(
'Data Collection: ${Faro().enableDataCollection ? "ENABLED" : "DISABLED"}'),
onPressed: () {
Faro().enableDataCollection = !Faro().enableDataCollection;
setState(() {}); // Refresh UI
},
),
const SizedBox(height: 16),
],
),
),
),
);
}
}