Logging in Xamarin application: Building infrastructure with MvvmCross
In my previous post about logging in Xamarin, I outlined what things need to be logged and how to log them in your mobile app.
The next important step is to ensure that the logging infrastructure follows the “Don’t repeat yourself” (DRY) and “Separation of concerns” (SoC) principles.
You can significantly reduce the long-term cost of codebase development and maintenance by using a simple logging API.
In this post, I will define the logging infrastructure implementation using MvvmCross, AppCenter, and Serilog in six steps:
- Define logging targets map
- Setup logging libraries
- Logging application events
- Logging core MvvmCross events (no action required)
- Logging method execution time (optional)
- Logging FFImageLoading events (bonus)
1. Define logging targets map
As the first step, it is crucial to decide what logging target will be used depending on the logging data type, current environment, and the event level.
For instance, reporting crashes during debugging is useless and leads to a blurry picture of the app state.
When using AppCenter, the logging targets mapping table looks like this:
2. Setup logging libraries
MvvmCross has its own logging mechanism that supports some popular logging frameworks, including Serilog.
NuGet packages needed at this step: Serilog
, Serilog.Sinks.File
, Serilog.Sinks.Xamarin
and SByteDev.Serilog.Sinks.AppCenter
.
The configuration is almost the same on the iOS and Android platforms:
The only difference being in the console sink: NSLog
on iOS and AndroidLog
on Android. This configuration writes all events to the console and file. AppCenter will also handle events with Information
level and higher.
For more information on creating logger configuration, see the Serilog documentation.
This implementation follows the defined targets map, but it does not care about the current app environment, e.g. the AppCenter sink will be enabled in Debug mode.
The DEBUG
symbol can be checked, or the simple Feature Registry plugin can be applied to activate sinks conditionally:
3. Logging application events
Now application-level events can be logged using a shared logger interface (IMvxLog
) that is accessible from any View Model or Service.
The MvxNavigationViewModel
base class is useful when a logger is required in the View Model, as it already contains the Log
property.
When the logger instance is needed at the Service level, it must be passed to the constructor or initialized manually through the IoCProvider
:
var logProvider = Mvx.IoCProvider.Resolve<IMvxLogProvider>();
var log = logProvider.GetLogFor("Name");
Here is a complete example of the View Model implementation:
- When the View is created, the View Model writes an informational message that will be redirected to App Center Analytics.
- Then it performs some dangerous operation and tracks its successful result (if any).
- In case of failure in the release version of the app, the error will be redirected to App Center Crashes.
All the logs will also be written to the console or file, depending on the current configuration.
4. Logging core MvvmCross events
It is useful for developers to keep the internal MvvmCross logs as well. Why is it important? There are several reasons:
- The most common issue is an error during the binding View Model to the View. If developers ignore a “binding failed” log message, they will never know why users stopped using the application: users will face an empty table, empty label, etc.
- Other useful messages are related to navigation failures. Yes, MvvmCross provides ViewModel-driven navigation, but it can fail in some cases, mainly if there are custom presenters.
- All third-party plugins also use this logging mechanism and often post events.
5. Logging method execution time
Users like apps that run smoothly. Another step for debugging performance is to register the execution time for some critical methods.
The easiest way to get method execution time is to use the Fody.MethodTimer library.
The only thing to do is to add the Time
attribute to the particular method and implement a custom interceptor that uses the internal IMvxLog
:
The logger output will look like this: SomeViewModel.ViewCreated 155 milliseconds
.
6. Logging FFImageLoading events
FFImageLoading is a popular cross-platform image loading library and provides some logging functionality.
Developers can track image processing failures and events by implementing the IMiniLogger
:
The implementation of IMiniLogger
needs to be set right in Setup.CreateLogProvider
method:
ImageService.Instance.Config.Logger = new ImageLoaderLogger(logProvider);
The logger will produce the output like this: Generating/retrieving image: https://images.com/image;CircleTransformation,borderSize=5,borderHexColor=#FF7F7F7F
.
Building a logging infrastructure for a Xamarin application is simple, and can be achieved using existing mechanisms, but adds significant value to the codebase quality.
Please share your custom logger implementations for popular libraries in the comments section below!