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
Link | Main Packages Include | Description |
---|---|---|
Echo | Logger, Core, Network, Bloc, Navigation, Storage | Main package that integrates all Echo functionalities |
Echo Core | Logger | Utilities and shared functionality used by other Echo packages |
Echo Logger | - | Mandatory package with core features and interfaces |
Echo Store | Isar/Hive CE, Core, Logger | Persistant storage allows you save data longer then one session |
Echo Bloc Observer | Flutter Bloc, Core, Logger | Enhance logging for Bloc state management |
Echo Network - Dio interceptor | Dio, Core, Logger | Enhance logging of API requests, responses and catch error |
Echo Navigation | Core, Logger | Enhance logging of navigation events |
Getting Started 🚀
- Installation process
- Available extensions
- Basic usage examples
- 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),
);
}
}