Developer

Echo - Embedit Logging Library

Monitoring and observability in one package. Save logs and share with someone who can help you. Report crashes and monitor what's happening in the app.

Features

We are using specific features to handle all the necessary tasks

  • Works with any crash reporting tool (Firebase Crashlytics, Sentry, custom tools, etc.)
  • Flutter custom interface for monitoring logs
  • Sharing, saving and crashes in session or more with persistant store
  • Colored console output with formating
  • Built-in support for Dio
  • Built-in support for Bloc / Flutter Bloc
  • Built-in support for Persistant store of your logs
  • Built-in support for Masking sensitive data like emails, ids, ip-addresses etc.

Packages

LinkMain Packages IncludeDescription
EchoLogger, Core, Network, Bloc, Navigation, StorageMain package that integrates all Echo functionalities
Echo CoreLoggerUtilities and shared functionality used by other Echo packages
Echo Logger-Mandatory package with core features and interfaces
Echo StoreIsar/Hive CE, Core, LoggerPersistant storage allows you save data longer then one session
Echo Bloc ObserverFlutter Bloc, Core, LoggerEnhance logging for Bloc state management
Echo Network - Dio interceptorDio, Core, LoggerEnhance logging of API requests, responses and catch error
Echo NavigationCore, LoggerEnhance logging of navigation events

Getting Started 🚀

  1. Installation process
  2. Available extensions
  3. Basic usage examples
  4. Advanced configuration

Installlation process

For logging purposes only. You can add to your pub just the mandatory eit_echo package.

    dart pub add eit_echo_logger --hosted-url=[hosted_url]

But for easy observation and much more utility we recommend adding the eit_echo package instead. It is also recommended to add one of the storage implementations so you don't have to reinvent the wheel.

    dart pub add eit_echo --hosted-url=[hosted_url]

Available extensions for Logger

Echo Bloc

    dart pub add eit_echo_bloc --hosted-url=[hosted_url]

Echo Network

    dart pub add eit_echo_network --hosted-url=[hosted_url]

Echo Navigation

    dart pub add eit_echo_navigation --hosted-url=[hosted_url]

Echo Store

Add only the interface

    dart pub add eit_echo_store --hosted-url=[hosted_url]

Or concrete implementations

  • Hive:
    • uses the Hive CE package
    • recommended for logs containing sensitive information and or in apps able to clean this storage frequently
    • fast read/write
    • has encryption support
    • caches logs in memory - this can lead to out of memory error if not cleaned up regularly
    dart pub add eit_echo_store_hive --hosted-url=[hosted_url]
  • Isar:
    • uses the Isar package
    • roughly 2 times slower read/write
    • doesn't have encryption support
    • doesn't use any runtime memory - out of memory error cannot happen
    • write operations are offloaded to a separate isolate to not overburden the UI frame calculations
    • write operations are also unawaited
    • recommended for heavily observed apps producing large amounts of logs
    dart pub add eit_echo_store_isar --hosted-url=[hosted_url]

Usage Examples

Logger Initialization

import 'package:echo_logger/echo_logger.dart';

final logger = Logger(
  settings: LoggerSettings(
    output: ConsoleOutput(),
    filter: DevelopmentFilter(),
  ),
);

void main() {
  logger.init();
  runApp(MyApp());
}

Echo Initialization

import 'package:eit_echo/eit_echo.dart';
import 'package:eit_echo_store_hive/eit_echo_store_hive.dart';

final rootNavigatorKey = GlobalKey<NavigatorState>();
late final EitEcho logger;

Future<EitEcho> setupEcho(GlobalKey<NavigatorState> navigatorKey) {
    const password = String.fromEnvironment(
      'echo_password',
      defaultValue: '000000',
    );
    return EitEcho.createInstance(
      EchoSettings(
        password: password,
        loggerSettings: LoggerSettings.general(
          output: FlutterConsoleOutput(),
          filter: kDebugMode ? DevelopmentFilter() : ProductionFilter(),
        ),
      ),
      navigatorKey: navigatorKey,
      store: HiveLogStore.initialize(), // 
    );
}

void main() async {
    logger = await setupEcho(rootNavigatorKey);
    runApp(MyApp());
    logger.dispose();
}

note - Logger and EitEcho implement the same interface so they are interchangeable in following examples.

Logging messages

logger.log(LogLevel.info, 'This is an info message');
logger.info('This is another info message');
logger.error('This is an error message');
logger.warning('This is a warning message');

Logging Bloc state changes with Echo Bloc

...
import 'package:eit_echo_bloc/eit_echo_bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

  ...
    Bloc.observer = EchoBlocObserver(logger: logger);
    runApp(MyApp());
  ...

Logging Network communication with Echo Network

...
import 'package:eit_echo_network/eit_echo_network.dart';

final dio = Dio(...);
  ...
    dio.interceptors.add(LogDioInterceptor(logger: logger));
    runApp(MyApp());
  ...

Logging user navigation with Echo Navigation

...
import 'package:eit_echo_navigation/eit_echo_navigation.dart';

  ...
  runApp(
    MaterialApp(
      ...,
      navigatorObservers: [LogNavigationObserver(logger: logger)],
    ),
  );

Storing logs

EitEcho manages storing of messages itself. In initialization example we've shown HiveLogStore however that's not the only way. Another one is of course, using IsarLogStore instead or just passing nothing (null). EitEcho has it's own default storage, but that one only implements in-memory storage and is more for debugging purposes. If none of these implementations suits your needs feel free to import the store interface and implement your own version. The storages accept LogData or it's derivatives which encapsulate all the information of a log.

import 'package:eit_echo_logger/eit_echo_logger.dart';
import 'package:eit_log_store_isar/eit_log_store_isar.dart';

class MyLogger extends Logger {
  MyLogger(
    this.storage, 
    this.sessionId, {
    super.settings
  });

  final LogStore storage;
  final String sessionId;

  @override
  void log(
    LogLevel level,
    dynamic message, {
    LogType? type,
    DateTime? time,
    Object? error,
    StackTrace? stackTrace,
    AnsiPen? customPen,
  }) {
    super.log(
      level, 
      message,
      type: type,
      time: time,
      error: error,
      stackTrace: stackTrace,
      customPen: customPen,
    );
    storage.add(
      LogData(
        type: type ?? LogType.message,
        sessionId: sessionId, 
        message: message,
        time: time,
        error: error == null ? null : LogErrorObject(
          type: error.runtimeType, 
          message: error.toString(),
        ),
        stackTrace: stackTrace,
      ),
    );
  }
}

late final Logger logger;

void main() async {
  logger = MyLogger(
    await IsarLogStore.initialize('logs'), 
    DateTime.now().toIso8601String(),
  );
  ...
  // Log any message and the logger will save it to the storage 
  logger.info('This message will persist across multiple app runs'); 
  // info() calls log() with LogType.message which in turn calls our storage
}

UI Console from Echo

eit_echo package provides a simple UI interface to view and manage logs. The following example implements a simple widget which on press opens a UI Console with log information.

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

class EchoButton extends StatelessWidget {
  const EchoButton({
    required this.echo, 
    super.key,
  });

  final EitEcho echo;

  @override
  Widget build(BuildContext context) {
    return IconButton(
      icon: const Icon(Icons.logo_dev_outlined),
      onPressed: () => echo.openConsole(context),
    );
  }
}

Copyright © 2025. All rights reserved.