What is BLoC Logging?
BLoC logging refers to the practice of recording events, state changes, and errors that occur within your BLoCs and Cubits. Using a custom BlocObserver, you can intercept every event dispatched, every state emitted, and any error thrown. This gives you deep visibility into your app's behavior, making debugging easier, helping you track user flows, and enabling integration with analytics and crash reporting tools.
Why Use Logging in BLoC?
- Debugging – Quickly identify what events led to a certain state.
- Audit Trails – Understand user actions and system behavior.
- Performance Monitoring – Track how often events are triggered.
- Crash Reporting – Attach BLoC context to crash reports.
- Analytics – Send meaningful user events to analytics platforms.
- Testing – Verify that events and states flow as expected during development.
Setting Up a Basic BlocObserver
Create a class that extends BlocObserver and override the methods you want to log. Then set it as the global observer before running the app.
Advanced Logging with Logging Levels
For production, you may want to control verbosity. You can use a logging package like logger or create your own with levels (debug, info, warning, error).
Logging Events with Custom Data
Sometimes you want to add context (e.g., user ID, session ID) to every log. You can extend the observer to include such data.
Integrating with Crash Reporting (Firebase Crashlytics)
You can send BLoC errors to crash reporting tools for analysis.
Logging Transitions vs Changes
onChange is called for every state change in any BlocBase (both BLoC and Cubit). onTransition is called only for BLoCs and provides the event that triggered the transition. Use onTransition when you need the event context.
Formatting Logs for Readability
To make logs easier to parse, use consistent formatting and colors. The logger package can output colored logs in the console.
Conditional Logging for Different Environments
You may want to enable verbose logging only in development. Use kReleaseMode to conditionally register observers.
Logging to Files or Remote Services
For production monitoring, you can send logs to a remote service (e.g., Sentry, Datadog). Use an observer that asynchronously sends data.
Best Practices
- Use a single observer – One global observer is enough; compose multiple handlers inside it if needed.
- Avoid heavy work in observers – Logging should be fast and non‑blocking.
- Don’t log sensitive data – Avoid logging passwords, tokens, or PII.
- Use log levels – Differentiate between debug, info, warning, error.
- Attach context – Include user ID, session, or feature flags in logs when possible.
- Format consistently – Makes logs easier to search and parse.
- Test observer in isolation – Ensure it doesn’t break the app if the logging service fails.
Common Mistakes
- ❌ Logging inside event handlers – Creates duplication and mixes concerns; use observer instead.
- ❌ Forgetting to call super methods – Breaks internal observer chain.
- ❌ Logging excessively in production – Can impact performance and fill storage.
- ❌ Not sanitizing logs – May expose sensitive information.
- ❌ Using print() instead of a proper logger – print is hard to control and disable.
- ❌ Blocking the main thread with I/O – If writing to disk or network, do it asynchronously.
Conclusion
BLoC logging with a custom BlocObserver gives you unparalleled insight into your app's state management. Whether you're debugging locally or monitoring in production, logging helps you understand user flows, catch errors early, and improve overall app quality. By following best practices and integrating with modern logging and crash reporting tools, you can build robust, observable Flutter applications.