Flutter Logging Plugin Example

The Flutter Logger package is a simple logging utility that allows you to log information and debug your Flutter applications. It provides different log levels like debug, info, warning, error, and critical, making it easy to categorize and filter logs. 

You can also customize the output format and destination of the logs, making it ideal for debugging complex applications.


Usage:

Small, easy to use and extensible logger which prints beautiful logs.


Code:


import 'package:flutter/material.dart';
import 'package:logger/logger.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    // var logger = Logger();
    var logger = Logger(
      printer: PrettyPrinter(
        methodCount: 0,
        errorMethodCount: 5,
        lineLength: 75,
        colors: true,
        printEmojis: true,
        printTime: true,
      ),
    );

    logger.d('Debug message');
    logger.i('Info message');
    logger.w('Warning message');
    logger.e('Error message');
    logger.wtf('Critical message');

    return MaterialApp(
      title: 'Flutter Logging Plugin',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Logging Plugin'),
        ),
        body: const Center(
          child: Text('Check console and log file for messages.'),
        ),
      ),
    );
  }
}

..


Properties:

  • printer: An object that specifies the output format for the logs. The PrettyPrinter class provides a human-readable format, while the JsonPrinter class provides a JSON format.
  • A property that sets the PrettyPrinter instance used to format the logs. In this example, a PrettyPrinter is created with the following options:
  • methodCount: The number of methods to include in the stack trace. In this example, it is set to 0 to exclude the stack trace.
  • errorMethodCount: The number of methods to include in the stack trace for error messages. In this example, it is set to 5.
  • lineLength: The maximum length of each log line. In this example, it is set to 75.
  • colors: A boolean value that indicates whether or not to include colors in the logs. In this example, it is set to true.
  • printEmojis: A boolean value that indicates whether or not to include emojis in the logs. In this example, it is set to true.
  • printTime: A boolean value that indicates whether or not to include time stamps in the logs. In this example, it is set to true.

The Logger instance with the PrettyPrinter is used to log messages at different levels of severity throughout the app.


I/flutter (22102): ┌──────────────────────────────────────────────────────────────────────────
I/flutter (22102): │ 09:15:38.534 (+0:00:00.001644)
I/flutter (22102): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
I/flutter (22102): │ 🐛 Debug message
I/flutter (22102): └──────────────────────────────────────────────────────────────────────────
I/flutter (22102): ┌──────────────────────────────────────────────────────────────────────────
I/flutter (22102): │ 09:15:38.540 (+0:00:00.007928)
I/flutter (22102): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
I/flutter (22102): │ 💡 Info message
I/flutter (22102): └──────────────────────────────────────────────────────────────────────────
I/flutter (22102): ┌──────────────────────────────────────────────────────────────────────────
I/flutter (22102): │ 09:15:38.540 (+0:00:00.008288)
I/flutter (22102): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
I/flutter (22102): │ ! Warning message
I/flutter (22102): └──────────────────────────────────────────────────────────────────────────
I/flutter (22102): ┌──────────────────────────────────────────────────────────────────────────
I/flutter (22102): │ 09:15:38.541 (+0:00:00.008499)
I/flutter (22102): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
I/flutter (22102): │ ⛔ Error message
I/flutter (22102): └──────────────────────────────────────────────────────────────────────────
I/flutter (22102): ┌──────────────────────────────────────────────────────────────────────────
I/flutter (22102): │ 09:15:38.541 (+0:00:00.008705)
I/flutter (22102): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
I/flutter (22102): │ 👾 Critical message
I/flutter (22102): └──────────────────────────────────────────────────────────────────────────
E/LB      (22102): fail to open file: No such file or directory
I/m.appdesign.pr(22102): ProcessProfilingInfo new_methods=1100 is saved saved_to_disk=1 resolve_classes_delay=8000

..
Flutter Plugins,


To create a common function that logs messages with different levels (e.g. info, warning, etc.), you can modify the log() function as follows:

Add an additional parameter to the log() function to specify the log level.

void logDebug(String message, {Level level = Level.info}) {
if (kDebugMode) { switch (level) { case Level.debug: logger.d(message); break; case Level.info: logger.i(message); break; case Level.warning: logger.w(message); break; case Level.error: logger.e(message); break; case Level.wtf: logger.wtf(message); break; } } }
..
Define an enum for the different log levels.

enum Level { debug, info, warning, error, wtf }
..
Call the log() function with the appropriate log level.
logDebug('Debug message', level: Level.debug);
logDebug('Info message', level: Level.info);
logDebug('Warning message', level: Level.warning);
logDebug('Error message', level: Level.error);
logDebug('Critical message', level: Level.wtf);
..
With this approach, you can use a single function to log messages with different levels, and you can easily add or modify the log levels as needed.

Creating a common function to handle logging has several advantages, some of which are:

Consistency: By using a common function, you can ensure that all logging in your application is consistent and follows the same pattern. This makes it easier to understand and analyze the logs.

Centralized configuration: If you need to change the logging behavior or settings, you can do it in one place (i.e., the common function) rather than modifying each logging statement individually.

Reusability: If you need to log messages in multiple places in your application, you can reuse the common function rather than duplicating code.

Flexibility: By passing parameters to the common function (e.g., log level), you can make it more flexible and adaptable to different scenarios.

Maintainability: If you need to update or modify the logging behavior in the future, you can do it in one place, which makes it easier to maintain your code.

Overall, creating a common function for logging can improve the consistency, maintainability, and flexibility of your code, and make it easier to manage logging behavior across your entire application.

..
Code:

import 'package:flutter/foundation.dart';
import 'package:logger/logger.dart';

var logger = Logger(
  printer: PrettyPrinter(
    methodCount: 0,
    errorMethodCount: 5,
    lineLength: 75,
    colors: true,
    printEmojis: true,
    printTime: true,
  ),
);

// * Define an enum for the different log levels.
enum Level { debug, info, warning, error, wtf }

// * Add an additional parameter to the log() function to specify the log level.
void log(String message, {Level level = Level.info}) {
  if (kDebugMode) {
    try{
      switch (level) {
        case Level.debug:
          logger.d(message);
          break;
        case Level.info:
          logger.i(message);
          break;
        case Level.warning:
          logger.w(message);
          break;
        case Level.error:
          logger.e(message);
          break;
        case Level.wtf:
          logger.wtf(message);
          break;
      }
    }catch(e){
      print(e);
    }
  }
}
..
In your widget:
  @override
  void initState() { // Called when the state object is inserted into the tree.
    super.initState();
    logDebug('Debug message', level: Level.debug);
    logDebug('Info message', level: Level.info);
    logDebug('Warning message', level: Level.warning);
    logDebug('Error message', level: Level.error);
    logDebug('Critical message', level: Level.wtf); 
  
  
..
Another Example:
Let us create a Dio instance with base options and logger interceptor


// Create a repository that handles fetching the data from the API
class UserRepository {
  // Create a Dio instance with base options and logger interceptor
  final Dio _dio = Dio(BaseOptions(
    baseUrl: 'https://jsonplaceholder.typicode.com',
    connectTimeout: const Duration(seconds:5),
    receiveTimeout: const Duration(seconds: 3),
    responseType: ResponseType.json,
  ))..interceptors.addAll([

     //AuthorizationInterceptor(),
     LoggerInterceptor(),
     //DioFirebasePerformanceInterceptor(),

  ]);

..
Full code:
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:logger/logger.dart';

// https://www.boltuix.com/2023/03/flutter-logging-plugin-example.html
// Initialize a logger instance with the specified configuration
var logger = Logger(
  printer: PrettyPrinter(
    methodCount: 0,
    errorMethodCount: 5,
    lineLength: 75,
    colors: true,
    printEmojis: true,
    printTime: true,
  ),
);

// Define an enum for the different log levels
enum Level { debug, info, warning, error, wtf }

// Define a logDebug function that logs messages at the specified level
void logDebug(String message, {Level level = Level.info}) {
  // Only log messages if app is running in debug mode
  if (kDebugMode) {
    try{
      switch (level) {
        case Level.debug:
          logger.d(message);
          break;
        case Level.info:
          logger.i(message);
          break;
        case Level.warning:
          logger.w(message);
          break;
        case Level.error:
          logger.e(message);
          break;
        case Level.wtf:
          logger.wtf(message);
          break;
      }
    }catch(e){
      print(e);
    }
  }
}

// Define an interceptor that logs the requests and responses
class LoggerInterceptor extends Interceptor {
  // Initialize a logger instance with the specified configuration
  Logger logger = Logger(
    printer: PrettyPrinter(
      methodCount: 0,
      errorMethodCount: 5,
      lineLength: 75,
      colors: true,
      printEmojis: true,
      printTime: false,
    ),
  );

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
    final options = err.requestOptions;
    final requestPath = '${options.baseUrl}${options.path}';

    // Log the error request and error message
    logDebug('${options.method} request => $requestPath', level: Level.error);
    logDebug('Error: ${err.error}, Message: ${err.message}', level: Level.debug);

    // Call the super class to continue handling the error
    return super.onError(err, handler);
  }

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    final requestPath = '${options.baseUrl}${options.path}';

    // Log the request
    logDebug('${options.method} request => $requestPath', level: Level.info);

    // Call the super class to continue handling the request
    return super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {

    // Log the response status code and data
    logDebug('StatusCode: ${response.statusCode}, Data: ${response.data}', level: Level.debug);

    // Call the super class to continue handling the response
    return super.onResponse(response, handler);
  }
}

..

GET source code on Github:

Comments