dart:developer log

Standard logging functionality built into Dart and Flutter. Designed to work closely with package:logging, provides integration with Flutter DevTools. Meets basic logging requirements without external dependencies, enables direct display in development tools.

Logging LibraryDartFlutterDevToolsDebugStandard

Library

dart:developer log

Overview

dart:developer log is the standard logging functionality built into Dart and Flutter, providing specialized features for debugging and performance measurement during development. Designed to work closely with package:logging, it enables direct display in developer tools through integration with Flutter DevTools. Meeting basic logging requirements without external dependencies, it supports detailed message output via log function, performance tracing through Timeline API, and custom event transmission via postEvent. It's valued in projects wanting to minimize dependencies and serves as a standard-included debugging solution for lightweight logging requirements.

Details

The dart:developer library provides a mechanism to communicate with Flutter DevTools via the Dart VM Service Protocol, collecting diagnostic information such as logs, performance data, and memory usage. Widely used as a basic debugging means in Flutter development in 2025, with enhanced debugging experience through DevTools integration. The log function can include more detailed granularity and richer information in logging output compared to print() or debugPrint(), providing structured information through parameters like name, time, sequenceNumber, level, error, and stackTrace. It integrates diverse functionality specialized for development and debugging, including custom performance tracing via Timeline.startSync/finishSync, arbitrary event data transmission via postEvent, and object inspection capabilities via inspect.

Key Features

  • Dart/Flutter standard inclusion: Immediate use without external packages
  • DevTools integration: Log display and inspection capabilities in Flutter DevTools
  • Structured logging: Rich metadata including name, level, timestamp
  • Performance tracing: Execution time measurement via Timeline API
  • Event transmission: Custom event data via postEvent
  • Object inspection: Detailed object display in DevTools

Advantages and Disadvantages

Advantages

  • Standard position in Dart/Flutter ecosystem with low learning cost
  • Lightweight nature without external dependencies reducing project complexity
  • Excellent integration with Flutter DevTools and intuitive debugging experience
  • Detailed information provision and filtering capabilities through structured logging
  • Accurate performance measurement and bottleneck identification via Timeline API
  • Continuity for consistent use from learning stages to full development

Disadvantages

  • Limitations in production environment log management and file output capabilities
  • Lack of advanced filtering and analysis features for large-scale applications
  • Absence of external log collection services and cloud integration features
  • Existence of print length limitations compared to print() (some environments)
  • Insufficient functionality for enterprise-level log analysis and monitoring requirements
  • Constraints in CI/CD environments due to DevTools dependency

Reference Pages

Code Examples

Basic Log Output

import 'dart:developer' as developer;

void main() {
  // Basic log output
  developer.log('Application has started');
  
  // Categorized logs
  developer.log('User authentication started', name: 'auth');
  developer.log('Database connection established', name: 'database');
  developer.log('API call completed', name: 'api');
  
  // Leveled logs
  developer.log('Debug information', name: 'debug', level: 500);
  developer.log('Info message', name: 'info', level: 800);
  developer.log('Warning message', name: 'warning', level: 900);
  developer.log('Error message', name: 'error', level: 1000);
  
  // Detailed variable information
  final user = User(id: 123, name: 'John Doe', email: '[email protected]');
  developer.log('User info: $user', name: 'user_data');
  
  // JSON format logs
  final userData = {
    'id': 123,
    'name': 'John Doe',
    'loginTime': DateTime.now().toIso8601String(),
    'permissions': ['read', 'write']
  };
  developer.log('User data', name: 'json_data', error: userData);
}

class User {
  final int id;
  final String name;
  final String email;
  
  User({required this.id, required this.name, required this.email});
  
  @override
  String toString() => 'User(id: $id, name: $name, email: $email)';
}

Error Handling and Stack Traces

import 'dart:developer' as developer;

void main() {
  // Exception handling and stack traces
  try {
    riskyOperation();
  } catch (error, stackTrace) {
    developer.log(
      'An error occurred',
      name: 'error_handler',
      error: error,
      stackTrace: stackTrace,
      level: 1000,
    );
  }
  
  // Detailed logging of custom exceptions
  try {
    validateUserInput('');
  } catch (error, stackTrace) {
    developer.log(
      'Validation error',
      name: 'validation',
      error: error,
      stackTrace: stackTrace,
      level: 900,
      time: DateTime.now(),
    );
  }
}

void riskyOperation() {
  throw Exception('Something went wrong');
}

void validateUserInput(String input) {
  if (input.isEmpty) {
    throw ArgumentError('Input is empty');
  }
}

// Custom exception class
class CustomException implements Exception {
  final String message;
  final String code;
  final Map<String, dynamic> details;
  
  CustomException(this.message, this.code, {this.details = const {}});
  
  @override
  String toString() => 'CustomException($code): $message';
}

// Function to log exceptions
void logException(Exception exception, StackTrace stackTrace, {String? context}) {
  developer.log(
    'Exception in ${context ?? "unknown context"}',
    name: 'exception',
    error: exception,
    stackTrace: stackTrace,
    level: 1000,
    time: DateTime.now(),
  );
}

Performance Measurement and Timeline

import 'dart:developer' as developer;

void main() async {
  // Basic timeline measurement
  developer.Timeline.startSync('Data loading');
  await loadData();
  developer.Timeline.finishSync();
  
  // Nested timeline measurement
  developer.Timeline.startSync('User processing');
  
  developer.Timeline.startSync('Authentication');
  await authenticateUser();
  developer.Timeline.finishSync();
  
  developer.Timeline.startSync('Permission check');
  await checkPermissions();
  developer.Timeline.finishSync();
  
  developer.Timeline.finishSync();
  
  // Function execution time measurement
  await measureFunction('API call', () async {
    await callApi();
  });
  
  // Comparative measurement of multiple processes
  await measureFunction('Database loading', () async {
    await loadFromDatabase();
  });
  
  await measureFunction('Cache loading', () async {
    await loadFromCache();
  });
}

Future<void> loadData() async {
  await Future.delayed(Duration(milliseconds: 100));
}

Future<void> authenticateUser() async {
  await Future.delayed(Duration(milliseconds: 50));
}

Future<void> checkPermissions() async {
  await Future.delayed(Duration(milliseconds: 30));
}

Future<void> callApi() async {
  await Future.delayed(Duration(milliseconds: 200));
}

Future<void> loadFromDatabase() async {
  await Future.delayed(Duration(milliseconds: 150));
}

Future<void> loadFromCache() async {
  await Future.delayed(Duration(milliseconds: 20));
}

// Generic execution time measurement function
Future<T> measureFunction<T>(String name, Future<T> Function() function) async {
  final stopwatch = Stopwatch()..start();
  developer.Timeline.startSync(name);
  
  try {
    final result = await function();
    stopwatch.stop();
    
    developer.log(
      '$name completed: ${stopwatch.elapsedMilliseconds}ms',
      name: 'performance',
      level: 800,
    );
    
    return result;
  } finally {
    developer.Timeline.finishSync();
  }
}

// Synchronous operation measurement
void measureSyncOperation() {
  developer.Timeline.startSync('Numerical calculation');
  
  var result = 0;
  for (int i = 0; i < 1000000; i++) {
    result += i;
  }
  
  developer.Timeline.finishSync();
  
  developer.log('Calculation result: $result', name: 'calculation');
}

Advanced Integration with DevTools

import 'dart:developer' as developer;
import 'dart:convert';

void main() {
  // Detailed object inspection
  final user = User(
    id: 123,
    name: 'John Doe',
    profile: UserProfile(
      age: 30,
      department: 'Development',
      skills: ['Flutter', 'Dart', 'Firebase'],
    ),
  );
  
  // Make object inspectable in DevTools
  developer.inspect(user);
  developer.log('User object inspection', name: 'object_inspection');
  
  // Custom event transmission
  sendCustomEvent('user_login', {
    'userId': user.id,
    'timestamp': DateTime.now().toIso8601String(),
    'source': 'mobile_app',
  });
  
  // Detailed logging of list data
  final items = List.generate(10, (index) => Item(id: index, name: 'Item $index'));
  developer.log('Generated item count: ${items.length}', name: 'data_generation');
  
  for (final item in items) {
    developer.inspect(item);
  }
  
  // State change logging
  logStateChange('loading', 'loaded', {'itemCount': items.length});
  
  // Performance metrics transmission
  sendPerformanceMetrics();
}

class User {
  final int id;
  final String name;
  final UserProfile profile;
  
  User({required this.id, required this.name, required this.profile});
  
  @override
  String toString() => 'User(id: $id, name: $name)';
}

class UserProfile {
  final int age;
  final String department;
  final List<String> skills;
  
  UserProfile({required this.age, required this.department, required this.skills});
  
  @override
  String toString() => 'UserProfile(age: $age, department: $department)';
}

class Item {
  final int id;
  final String name;
  
  Item({required this.id, required this.name});
  
  @override
  String toString() => 'Item(id: $id, name: $name)';
}

// Custom event transmission
void sendCustomEvent(String eventName, Map<String, dynamic> data) {
  developer.postEvent(eventName, data);
  developer.log(
    'Event sent: $eventName',
    name: 'events',
    error: data,
    level: 700,
  );
}

// State change logging
void logStateChange(String from, String to, Map<String, dynamic> context) {
  final changeData = {
    'from': from,
    'to': to,
    'timestamp': DateTime.now().toIso8601String(),
    'context': context,
  };
  
  developer.postEvent('state_change', changeData);
  developer.log(
    'State change: $from -> $to',
    name: 'state',
    error: changeData,
    level: 600,
  );
}

// Performance metrics transmission
void sendPerformanceMetrics() {
  final metrics = {
    'memory_usage': 'unknown', // In actual implementation, get appropriate metrics
    'frame_rate': 60,
    'build_time': DateTime.now().millisecondsSinceEpoch,
  };
  
  developer.postEvent('performance_metrics', metrics);
  developer.log(
    'Performance metrics sent',
    name: 'performance',
    error: metrics,
    level: 500,
  );
}

Practical Examples in Flutter Applications

import 'dart:developer' as developer;
import 'package:flutter/material.dart';

void main() {
  developer.log('Flutter application started', name: 'app_lifecycle');
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    developer.log('MyApp build method called', name: 'widget_lifecycle');
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() {
    developer.log('MyHomePage State created', name: 'widget_lifecycle');
    return _MyHomePageState();
  }
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  
  @override
  void initState() {
    super.initState();
    developer.log('MyHomePage initState', name: 'widget_lifecycle');
  }
  
  @override
  void dispose() {
    developer.log('MyHomePage dispose', name: 'widget_lifecycle');
    super.dispose();
  }
  
  void _incrementCounter() {
    developer.Timeline.startSync('Counter update');
    
    setState(() {
      _counter++;
    });
    
    developer.Timeline.finishSync();
    
    developer.log(
      'Counter updated: $_counter',
      name: 'user_interaction',
      level: 600,
      time: DateTime.now(),
    );
    
    // Custom event transmission
    developer.postEvent('counter_increment', {
      'newValue': _counter,
      'timestamp': DateTime.now().toIso8601String(),
    });
  }
  
  @override
  Widget build(BuildContext context) {
    developer.Timeline.startSync('MyHomePage build');
    
    final widget = Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo Home Page'),
      ),
      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,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
    
    developer.Timeline.finishSync();
    return widget;
  }
}

// Network request logging
class ApiService {
  static Future<Map<String, dynamic>> fetchUserData(int userId) async {
    developer.log('API call started: User data fetch', name: 'api');
    developer.Timeline.startSync('User data fetch API');
    
    try {
      // Mock API call
      await Future.delayed(Duration(milliseconds: 500));
      
      final userData = {
        'id': userId,
        'name': 'John Doe',
        'email': '[email protected]',
      };
      
      developer.log(
        'API call successful: User data fetch',
        name: 'api',
        error: userData,
        level: 800,
      );
      
      return userData;
      
    } catch (error, stackTrace) {
      developer.log(
        'API call error: User data fetch',
        name: 'api',
        error: error,
        stackTrace: stackTrace,
        level: 1000,
      );
      rethrow;
    } finally {
      developer.Timeline.finishSync();
    }
  }
}

// Log level-based filtering
class Logger {
  static const int DEBUG = 500;
  static const int INFO = 800;
  static const int WARNING = 900;
  static const int ERROR = 1000;
  
  static void debug(String message, {String name = 'debug'}) {
    developer.log(message, name: name, level: DEBUG);
  }
  
  static void info(String message, {String name = 'info'}) {
    developer.log(message, name: name, level: INFO);
  }
  
  static void warning(String message, {String name = 'warning'}) {
    developer.log(message, name: name, level: WARNING);
  }
  
  static void error(String message, {Object? error, StackTrace? stackTrace, String name = 'error'}) {
    developer.log(
      message,
      name: name,
      level: ERROR,
      error: error,
      stackTrace: stackTrace,
    );
  }
}

Conditional Logging for Production

import 'dart:developer' as developer;
import 'package:flutter/foundation.dart';

void main() {
  // Log output only in debug mode
  if (kDebugMode) {
    developer.log('Running in debug mode', name: 'app_mode');
  }
  
  // Performance logs in profile mode
  if (kProfileMode) {
    developer.log('Running in profile mode', name: 'app_mode');
  }
  
  // Only error logs in release mode
  if (kReleaseMode) {
    developer.log('Running in release mode', name: 'app_mode');
  }
  
  runApp(MyApp());
}

// Conditional log output class
class ConditionalLogger {
  static void log(String message, {String name = 'app', int level = 800}) {
    // Detailed logs only in debug mode
    if (kDebugMode) {
      developer.log(message, name: name, level: level);
    }
  }
  
  static void errorLog(String message, {Object? error, StackTrace? stackTrace}) {
    // Error logs in all modes
    developer.log(
      message,
      name: 'error',
      level: 1000,
      error: error,
      stackTrace: stackTrace,
    );
  }
  
  static void performanceLog(String operation, int durationMs) {
    // Performance logs in profile and debug modes
    if (kDebugMode || kProfileMode) {
      developer.log(
        '$operation completed: ${durationMs}ms',
        name: 'performance',
        level: 600,
      );
    }
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ConditionalLogger.log('MyApp build started');
    
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ConditionalLogger.log('MyHomePage build started');
    
    return Scaffold(
      appBar: AppBar(title: Text('Conditional Logging Demo')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () {
                ConditionalLogger.log('Button was pressed');
                // Intentionally cause an error
                try {
                  throw Exception('Test error');
                } catch (error, stackTrace) {
                  ConditionalLogger.errorLog(
                    'Error occurred during button processing',
                    error: error,
                    stackTrace: stackTrace,
                  );
                }
              },
              child: Text('Log Test'),
            ),
          ],
        ),
      ),
    );
  }
}