Talker
Handy toolkit for mobile app debugging. Inspired by web inspectors and customized for mobile application development. Modern solution providing comprehensive debugging environment through simple yet versatile functionality.
Library
Talker
Overview
Talker is a "high-performance error handling and logging library for Flutter/Dart" developed as a comprehensive debugging and monitoring solution for Flutter application development. With the concept of "Logs for Humans," it integrates recording and management of UI, HTTP requests, errors, and other information, providing essential features for Flutter developers including app monitoring, log history preservation, report sharing, custom logs, and colorful display customization.
Details
Talker 2025 edition has established a solid position as the definitive debugging and monitoring tool in the Flutter/Dart ecosystem. Providing a simple and intuitive API, it centralizes exception handling, log management, and error tracking within applications. It offers rich integration features with major libraries and patterns used in Flutter development, including automatic logging of HTTP requests/responses via Dio, integration with BLoC patterns, and connectivity with Riverpod. It comprehensively provides practical features that significantly improve development and debugging workflows, such as in-app log UI display, log history sharing and saving, and UI exception alert display.
Key Features
- Comprehensive Log Management: Integrated management of UI, HTTP, error, and custom logs
- Rich UI Display: Beautiful log display and customization through TalkerScreen
- Automatic Integration Support: Built-in connectivity with Dio HTTP, BLoC, and Riverpod
- Error Tracking and Reporting: Integration with Firebase Crashlytics, Sentry, and others
- Color Customization: 8 default log types with complete custom support
- Streaming Support: Real-time log monitoring and alert features
Pros and Cons
Pros
- Rich integration options and community support in Flutter/Dart ecosystem
- Efficient debugging experience and log management through intuitive and beautiful UI
- Simplified communication debugging through native HTTP log integration with Dio
- Automated state management logging through dedicated packages for BLoC and Riverpod
- Professional error analysis through integration with Firebase Crashlytics and Sentry
- High reliability and stability with 100% test coverage
Cons
- Flutter-specific library, not usable on other platforms
- Increased memory usage and performance impact with high-volume log output
- Initial setup complexity due to rich customization features
- Security risks from detailed log output in production environments
- Need for proper management and privacy considerations for log saving/sharing features
- Impact on app size due to many dependency libraries
Reference Pages
Code Examples
Installation and Setup (pubspec.yaml addition)
# Add Talker dependencies to pubspec.yaml
dependencies:
flutter:
sdk: flutter
# Core Talker package
talker: ^4.0.0
# Flutter UI integration
talker_flutter: ^4.0.0
# HTTP logging (optional, if using Dio)
talker_dio_logger: ^4.0.0
# BLoC integration (optional, if using BLoC)
talker_bloc_logger: ^4.0.0
# Riverpod integration (optional, if using Riverpod)
talker_riverpod_logger: ^4.0.0
dev_dependencies:
flutter_test:
sdk: flutter
# Install packages
# flutter pub get
// Initialize Talker in main.dart
import 'package:flutter/material.dart';
import 'package:talker_flutter/talker_flutter.dart';
// Define Talker instance globally
final talker = TalkerFlutter.init();
void main() {
// Configure Flutter error handling
FlutterError.onError = (FlutterErrorDetails details) {
talker.handle(details.exception, details.stack);
};
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Talker Demo',
// Add Talker's NavigatorObserver for navigation logs
navigatorObservers: [
TalkerRouteObserver(talker),
],
home: MyHomePage(),
// Configure global error handler
builder: (context, child) {
return TalkerWrapper(
talker: talker,
child: child!,
);
},
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
@override
void initState() {
super.initState();
// App startup log
talker.info('App started successfully');
}
void _incrementCounter() {
setState(() {
_counter++;
talker.info('Counter incremented: $_counter');
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Talker Demo'),
actions: [
// Button to navigate to log screen
IconButton(
icon: Icon(Icons.bug_report),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => TalkerScreen(talker: talker),
),
);
},
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
ElevatedButton(
onPressed: () {
// Example of intentional error
try {
throw Exception('Test exception');
} catch (e, st) {
talker.handle(e, st);
}
},
child: Text('Test Error'),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Basic Log Output
import 'package:talker/talker.dart';
// Create Talker instance
final talker = Talker();
void basicLoggingExample() {
// Info log
talker.info('Application started successfully');
// Warning log
talker.warning('Memory usage is getting high 🚨');
// Debug log
talker.debug('Starting user authentication process 🔍');
// Success log
talker.good('Data saving completed ✅');
// Error log (simple string)
talker.error('Network connection failed ❌');
// Custom log type
talker.log('Custom message', title: 'CUSTOM');
// Log with specified level
talker.logTyped(
TalkerLog(
'Important process started',
logLevel: LogLevel.critical,
title: 'PROCESS',
),
);
// Structured log (map data)
talker.info({
'event': 'user_login',
'user_id': '12345',
'timestamp': DateTime.now().toIso8601String(),
'success': true,
});
// Multi-line log
talker.info('''
Detailed multi-line information:
- User: John Doe
- Operation: Data update
- Result: Success
''');
}
// Exception and error handling
void errorHandlingExample() {
try {
// Some risky operation
performRiskyOperation();
} catch (error, stackTrace) {
// Record error with detailed stack trace
talker.handle(error, stackTrace, 'Error during user data processing');
}
// Conditional logging
if (someCondition) {
talker.warning('Condition matched: attention required');
}
// Output control based on log level
talker.configure(
settings: TalkerSettings(
logLevel: LogLevel.info, // Output only info level and above
),
);
}
void performRiskyOperation() {
throw Exception('Sample exception');
}
bool someCondition = true;
HTTP Request Logging
import 'package:dio/dio.dart';
import 'package:talker_dio_logger/talker_dio_logger.dart';
import 'package:talker/talker.dart';
// Talker and Dio integration setup
void setupHttpLogging() {
final talker = Talker();
final dio = Dio();
// Basic Dio Talker log configuration
dio.interceptors.add(
TalkerDioLogger(
talker: talker,
settings: const TalkerDioLoggerSettings(
printRequestHeaders: true,
printResponseHeaders: true,
printResponseMessage: true,
),
),
);
// Advanced HTTP log configuration
dio.interceptors.add(
TalkerDioLogger(
talker: talker,
settings: TalkerDioLoggerSettings(
// Request detail settings
printRequestHeaders: true,
printRequestData: true,
printResponseHeaders: true,
printResponseData: true,
printResponseMessage: true,
// Log color settings
requestPen: AnsiPen()..blue(),
responsePen: AnsiPen()..green(),
errorPen: AnsiPen()..red(),
// Filtering settings
requestFilter: (RequestOptions options) {
// Exclude requests containing '/secure' path
return !options.path.contains('/secure');
},
responseFilter: (Response response) {
// Exclude 301 redirect responses
return response.statusCode != 301;
},
// Error filter
errorFilter: (DioException error) {
// Log only specific error types
return error.type != DioExceptionType.cancel;
},
),
),
);
}
// Actual HTTP request examples
void httpRequestExample() async {
final dio = Dio();
try {
// GET request
talker.info('Starting user data fetch');
final response = await dio.get(
'https://jsonplaceholder.typicode.com/users',
queryParameters: {'_limit': 10},
);
talker.good('User data fetch completed: ${response.data.length} items');
// POST request
talker.info('Starting new post creation');
final postResponse = await dio.post(
'https://jsonplaceholder.typicode.com/posts',
data: {
'title': 'Talker Test Post',
'body': 'This is a Talker HTTP log test',
'userId': 1,
},
);
if (postResponse.statusCode == 201) {
talker.good('Post creation successful: ID=${postResponse.data['id']}');
}
} catch (e) {
talker.error('HTTP request error: $e');
}
}
// Custom HTTP log class
class CustomHttpLogger {
final Talker talker;
CustomHttpLogger(this.talker);
void logRequest(String method, String url, Map<String, dynamic>? data) {
talker.log(
'HTTP $method $url${data != null ? '\nBody: $data' : ''}',
title: 'HTTP_REQ',
);
}
void logResponse(int statusCode, String url, dynamic data) {
final isSuccess = statusCode >= 200 && statusCode < 300;
if (isSuccess) {
talker.good('HTTP $statusCode $url');
} else {
talker.error('HTTP $statusCode $url\nResponse: $data');
}
}
void logError(String method, String url, String error) {
talker.handle(
Exception('HTTP $method $url failed: $error'),
null,
'HTTP Request Error',
);
}
}
// Custom HTTP logging usage example
void customHttpLoggingExample() async {
final logger = CustomHttpLogger(talker);
logger.logRequest('GET', '/api/users', null);
try {
// Execute HTTP request
// ...
logger.logResponse(200, '/api/users', {'count': 10});
} catch (e) {
logger.logError('GET', '/api/users', e.toString());
}
}
Error Handling
import 'package:flutter/material.dart';
import 'package:talker_flutter/talker_flutter.dart';
// Comprehensive error handling configuration
class ErrorHandlingService {
static final Talker talker = TalkerFlutter.init(
settings: TalkerSettings(
// Disable detailed logs in production
enabled: true,
useConsoleLogs: true,
maxHistoryItems: 1000,
),
);
// Initialize app-wide error handling
static void initializeErrorHandling() {
// Flutter framework errors
FlutterError.onError = (FlutterErrorDetails details) {
talker.handle(
details.exception,
details.stack,
'Flutter Framework Error',
);
// Send to external services (Crashlytics, etc.) in production
if (isProduction) {
sendToCrashlytics(details);
}
};
// Dart async errors
PlatformDispatcher.instance.onError = (error, stack) {
talker.handle(error, stack, 'Dart Async Error');
return true;
};
// Zone errors (optional)
runZonedGuarded(
() {
runApp(MyApp());
},
(error, stack) {
talker.handle(error, stack, 'Zone Caught Error');
},
);
}
// Custom exception classes
static void handleBusinessError(String operation, Exception error) {
talker.error('Business Error[$operation]: ${error.toString()}');
// Display user-facing messages
showErrorDialog(operation, error);
}
// Network error specific handling
static void handleNetworkError(String endpoint, dynamic error) {
String message = 'Network Error[$endpoint]';
if (error is DioException) {
switch (error.type) {
case DioExceptionType.connectionTimeout:
message += ': Connection timeout';
break;
case DioExceptionType.receiveTimeout:
message += ': Receive timeout';
break;
case DioExceptionType.badResponse:
message += ': Server error(${error.response?.statusCode})';
break;
default:
message += ': ${error.message}';
}
}
talker.error(message);
}
// Recoverable error handling
static Future<T?> handleRecoverableError<T>(
String operation,
Future<T> Function() action,
{int maxRetries = 3}
) async {
for (int attempt = 0; attempt < maxRetries; attempt++) {
try {
talker.debug('Executing $operation... (attempt ${attempt + 1}/$maxRetries)');
final result = await action();
if (attempt > 0) {
talker.good('$operation succeeded on retry (attempt ${attempt + 1})');
}
return result;
} catch (error, stackTrace) {
talker.warning('$operation failed (attempt ${attempt + 1}/$maxRetries): $error');
if (attempt == maxRetries - 1) {
talker.handle(error, stackTrace, '$operation failed on final attempt');
rethrow;
}
// Wait with exponential backoff
await Future.delayed(Duration(seconds: 2 << attempt));
}
}
return null;
}
}
// Error-specific dialog
void showErrorDialog(String operation, Exception error) {
// Implementation depends on environment
talker.info('Showing error dialog: $operation');
}
// External service sending (e.g., Firebase Crashlytics)
void sendToCrashlytics(FlutterErrorDetails details) {
// Actual implementation would send to Firebase Crashlytics
talker.info('Sending error to Crashlytics: ${details.exception}');
}
bool get isProduction => const bool.fromEnvironment('dart.vm.product');
// Usage examples
void errorHandlingUsageExample() async {
// Basic error handling
try {
await riskyOperation();
} catch (e, st) {
ErrorHandlingService.talker.handle(e, st, 'Error in risky operation');
}
// Business error handling
try {
validateUserInput('');
} on ValidationException catch (e) {
ErrorHandlingService.handleBusinessError('User input validation', e);
}
// Recoverable error handling
final result = await ErrorHandlingService.handleRecoverableError(
'Data sync',
() => syncDataFromServer(),
maxRetries: 5,
);
if (result != null) {
ErrorHandlingService.talker.good('Data sync completed');
}
}
Future<void> riskyOperation() async {
throw Exception('Something went wrong');
}
void validateUserInput(String input) {
if (input.isEmpty) {
throw ValidationException('Input is empty');
}
}
Future<String> syncDataFromServer() async {
// Data sync from server (may fail)
if (DateTime.now().millisecond % 3 == 0) {
throw Exception('Server connection error');
}
return 'Data sync completed';
}
class ValidationException implements Exception {
final String message;
ValidationException(this.message);
@override
String toString() => 'ValidationException: $message';
}
UI Integration (TalkerScreen)
import 'package:flutter/material.dart';
import 'package:talker_flutter/talker_flutter.dart';
// Customized TalkerScreen implementation
class CustomTalkerScreen extends StatefulWidget {
final Talker talker;
const CustomTalkerScreen({Key? key, required this.talker}) : super(key: key);
@override
State<CustomTalkerScreen> createState() => _CustomTalkerScreenState();
}
class _CustomTalkerScreenState extends State<CustomTalkerScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('App Logs'),
backgroundColor: Colors.blueGrey[800],
foregroundColor: Colors.white,
actions: [
// Log clear button
IconButton(
icon: const Icon(Icons.clear_all),
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Clear Logs'),
content: const Text('Delete all logs?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
widget.talker.cleanHistory();
Navigator.pop(context);
widget.talker.info('Log history cleared');
},
child: const Text('Delete'),
),
],
),
);
},
),
// Log share button
IconButton(
icon: const Icon(Icons.share),
onPressed: () => _shareLogHistory(),
),
// Settings button
IconButton(
icon: const Icon(Icons.settings),
onPressed: () => _showLogSettings(),
),
],
),
body: TalkerScreen(
talker: widget.talker,
// Apply custom theme
theme: TalkerScreenTheme(
backgroundColor: Colors.grey[900]!,
cardColor: Colors.grey[800]!,
textColor: Colors.white,
logColors: {
// HTTP request/response colors
TalkerLogType.httpRequest.key: Colors.blue[300]!,
TalkerLogType.httpResponse.key: Colors.green[300]!,
// Error level color settings
TalkerLogType.error.key: Colors.red[400]!,
TalkerLogType.critical.key: Colors.red[600]!,
TalkerLogType.warning.key: Colors.orange[400]!,
TalkerLogType.info.key: Colors.blue[200]!,
TalkerLogType.debug.key: Colors.grey[400]!,
TalkerLogType.verbose.key: Colors.grey[600]!,
// Custom log type colors
'CUSTOM': Colors.purple[300]!,
'BUSINESS': Colors.teal[300]!,
'PERFORMANCE': Colors.yellow[300]!,
},
),
// Custom builder for log items
itemsBuilder: (context, data) {
return TalkerDataCards(
data: data,
onCopyTap: (text) {
_copyToClipboard(text);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Log copied to clipboard')),
);
},
);
},
),
// Floating action button to add test logs
floatingActionButton: FloatingActionButton.extended(
onPressed: _addTestLogs,
icon: const Icon(Icons.add),
label: const Text('Test Logs'),
),
);
}
void _shareLogHistory() {
final logs = widget.talker.history.formatted;
// Actual implementation would use share_plus package, etc.
widget.talker.info('Sharing log history (${logs.length} lines)');
// Simulation: actual implementation would share files or send emails
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Share Logs'),
content: Text('${logs.length} lines of logs prepared for sharing'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
),
);
}
void _showLogSettings() {
showModalBottomSheet(
context: context,
builder: (context) => Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Log Settings',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
ListTile(
title: const Text('Log Level'),
subtitle: const Text('Minimum level of logs to display'),
trailing: DropdownButton<LogLevel>(
value: LogLevel.verbose,
items: LogLevel.values.map((level) {
return DropdownMenuItem(
value: level,
child: Text(level.name),
);
}).toList(),
onChanged: (level) {
if (level != null) {
widget.talker.configure(
settings: TalkerSettings(logLevel: level),
);
Navigator.pop(context);
}
},
),
),
ListTile(
title: const Text('History Size'),
subtitle: const Text('Maximum number of logs to retain'),
trailing: const Text('1000 items'),
onTap: () {
// History size setting dialog
Navigator.pop(context);
},
),
],
),
),
);
}
void _copyToClipboard(String text) {
// Actual implementation would use Clipboard.setData()
widget.talker.debug('Copied to clipboard: ${text.substring(0, 50)}...');
}
void _addTestLogs() {
widget.talker.info('Adding test logs');
widget.talker.warning('This is a warning message');
widget.talker.error('This is an error message');
widget.talker.good('This is a success message');
widget.talker.debug('This is a debug message');
// Custom log
widget.talker.log(
'Custom business logic log',
title: 'BUSINESS',
);
// Structured data
widget.talker.info({
'event': 'test_log_added',
'timestamp': DateTime.now().toIso8601String(),
'data': {'count': 5, 'type': 'sample'}
});
}
}
// TalkerScreen embedding example (integration into other screens)
class DebugDrawer extends StatelessWidget {
final Talker talker;
const DebugDrawer({Key? key, required this.talker}) : super(key: key);
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue[600],
),
child: const Text(
'Debug Menu',
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
Expanded(
child: TalkerScreen(
talker: talker,
theme: const TalkerScreenTheme(
backgroundColor: Colors.white,
cardColor: Color(0xFFF5F5F5),
textColor: Colors.black87,
),
),
),
Container(
padding: const EdgeInsets.all(8),
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () => talker.cleanHistory(),
child: const Text('Clear Logs'),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomTalkerScreen(talker: talker),
),
);
},
child: const Text('Detail View'),
),
),
],
),
),
],
),
);
}
}
// Main screen TalkerScreen integration example
class MainScreenWithTalker extends StatelessWidget {
final Talker talker;
const MainScreenWithTalker({Key? key, required this.talker}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Main App'),
actions: [
// Show log button only in debug mode
if (!const bool.fromEnvironment('dart.vm.product'))
IconButton(
icon: Stack(
children: [
const Icon(Icons.bug_report),
// Error count badge
TalkerBuilder(
talker: talker,
builder: (context, data) {
final errorCount = data
.where((log) => log.logLevel.index >= LogLevel.error.index)
.length;
if (errorCount > 0) {
return Positioned(
right: 0,
top: 0,
child: Container(
padding: const EdgeInsets.all(2),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Text(
'$errorCount',
style: const TextStyle(
color: Colors.white,
fontSize: 10,
),
),
),
);
}
return const SizedBox.shrink();
},
),
],
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomTalkerScreen(talker: talker),
),
);
},
),
],
),
// Debug drawer
endDrawer: !const bool.fromEnvironment('dart.vm.product')
? DebugDrawer(talker: talker)
: null,
body: const Center(
child: Text('Main Content'),
),
);
}
}
Custom Log Configuration
import 'package:talker/talker.dart';
import 'package:talker_flutter/talker_flutter.dart';
// Custom log type definitions
class BusinessLog extends TalkerLog {
BusinessLog(String message) : super(message);
@override
String get key => 'BUSINESS';
@override
String get title => 'Business Logic';
@override
AnsiPen get pen => AnsiPen()..magenta();
}
class PerformanceLog extends TalkerLog {
final int duration;
final String operation;
PerformanceLog(this.operation, this.duration)
: super('$operation execution time: ${duration}ms');
@override
String get key => 'PERFORMANCE';
@override
String get title => 'Performance';
@override
AnsiPen get pen => AnsiPen()..yellow();
}
class SecurityLog extends TalkerLog {
final String userId;
final String action;
SecurityLog(this.action, {required this.userId})
: super('Security event: $action [User: $userId]');
@override
String get key => 'SECURITY';
@override
String get title => 'Security';
@override
AnsiPen get pen => AnsiPen()..red();
}
// Custom Talker configuration class
class AppTalkerConfig {
static Talker createTalker() {
return TalkerFlutter.init(
settings: TalkerSettings(
// Basic settings
enabled: true,
useConsoleLogs: true,
useHistory: true,
maxHistoryItems: 1000,
// Log level settings (adjust for production)
logLevel: _getLogLevel(),
// Log formatter
loggerFormatter: CustomLogFormatter(),
// Log filter
loggerFilter: CustomLogFilter(),
),
// Register custom log types
logger: TalkerLogger(
settings: TalkerLoggerSettings(
enableColors: true,
lineLength: 120,
),
),
);
}
static LogLevel _getLogLevel() {
// Environment-based log level configuration
if (const bool.fromEnvironment('dart.vm.product')) {
return LogLevel.warning; // Production: warning and above only
} else if (const bool.fromEnvironment('dart.vm.profile')) {
return LogLevel.info; // Profile: info and above
} else {
return LogLevel.verbose; // Debug: everything
}
}
}
// Custom log formatter
class CustomLogFormatter implements LoggerFormatter {
@override
String fmt(LogDetails details, TalkerLoggerSettings settings) {
final time = DateTime.now().toString().substring(11, 23);
final level = details.logLevel.name.toUpperCase().padRight(7);
final title = details.title?.padRight(10) ?? ''.padRight(10);
return '[$time] [$level] [$title] ${details.message}';
}
}
// Custom log filter
class CustomLogFilter implements LoggerFilter {
// Keywords to filter
static const List<String> _sensitiveKeywords = [
'password',
'token',
'secret',
'key',
'credential',
];
@override
bool shouldLog(LogDetails details) {
final message = details.message.toString().toLowerCase();
// Exclude logs containing sensitive information
if (_sensitiveKeywords.any((keyword) => message.contains(keyword))) {
return false;
}
// Exclude specific log types in production
if (const bool.fromEnvironment('dart.vm.product')) {
if (details.title == 'DEBUG' || details.title == 'VERBOSE') {
return false;
}
}
return true;
}
}
// Performance measurement with logging
class PerformanceLogger {
final Talker talker;
final Map<String, DateTime> _startTimes = {};
PerformanceLogger(this.talker);
void startOperation(String operationName) {
_startTimes[operationName] = DateTime.now();
talker.debug('Performance measurement started: $operationName');
}
void endOperation(String operationName) {
final startTime = _startTimes.remove(operationName);
if (startTime != null) {
final duration = DateTime.now().difference(startTime).inMilliseconds;
talker.logTyped(PerformanceLog(operationName, duration));
// Warning for slow operations
if (duration > 1000) {
talker.warning('Slow operation detected: $operationName (${duration}ms)');
}
}
}
Future<T> measureAsync<T>(
String operationName,
Future<T> Function() operation,
) async {
startOperation(operationName);
try {
final result = await operation();
endOperation(operationName);
return result;
} catch (e) {
endOperation(operationName);
rethrow;
}
}
}
// Usage examples and setup
void customLoggingExample() {
// Create custom Talker
final talker = AppTalkerConfig.createTalker();
// Create performance logger
final perfLogger = PerformanceLogger(talker);
// Custom log usage examples
talker.logTyped(BusinessLog('Starting user authentication process'));
talker.logTyped(SecurityLog('Login attempt', userId: 'user123'));
// Performance measurement example
perfLogger.startOperation('Database query');
// Some processing...
Future.delayed(Duration(milliseconds: 500), () {
perfLogger.endOperation('Database query');
});
// Async process measurement
perfLogger.measureAsync('File reading', () async {
await Future.delayed(Duration(milliseconds: 200));
return 'File data';
});
// Structured log example
talker.info({
'event': 'user_action',
'user_id': 'user123',
'action': 'purchase',
'item_id': 'item456',
'amount': 1200,
'timestamp': DateTime.now().toIso8601String(),
'metadata': {
'platform': 'mobile',
'version': '1.2.3',
},
});
// Log level usage examples
talker.verbose('Detailed debug information');
talker.debug('Debug information');
talker.info('General information');
talker.warning('Attention required situation');
talker.error('Error occurred');
// Custom title log
talker.log('Custom message', title: 'CUSTOM');
// Conditional logging
if (shouldLogDetailedInfo()) {
talker.debug('Detailed info: Database connection pool usage 85%');
}
// Real-time log monitoring example
talker.stream.listen((log) {
if (log.logLevel.index >= LogLevel.error.index) {
// Send error level and above logs to external service
sendToExternalService(log);
}
});
}
bool shouldLogDetailedInfo() {
// Enable detailed logs in debug mode or specific conditions
return !const bool.fromEnvironment('dart.vm.product');
}
void sendToExternalService(TalkerDataInterface log) {
// Send to external log aggregation services (Splunk, ELK, Datadog, etc.)
print('Sending log to external service: ${log.message}');
}