From db7ca3b93e20875618f109b96f692bbb285e1cd6 Mon Sep 17 00:00:00 2001 From: Federico Maccaroni Date: Wed, 2 Mar 2022 14:15:16 -0300 Subject: [PATCH] BEEEP: Abstract and Centralize Logging (#1663) * Abstracted App Center Logging into its own component, so that we can have it centralized in one place and we avoid checking for FDroid on all the places we want to use it * Implemented the new logger where Crashes.TrackError was being used except on some specific cases * Improved logging, added a debug logger and removed AppCenter to be used on DEBUG --- src/Android/Autofill/AutofillService.cs | 20 ++---- src/Android/MainActivity.cs | 2 +- src/Android/MainApplication.cs | 12 +++- src/Android/Services/AndroidLogService.cs | 2 +- .../AccountSwitchingOverlayView.xaml.cs | 19 ++--- .../AccountSwitchingOverlayViewModel.cs | 8 +-- .../Pages/Accounts/DeleteAccountViewModel.cs | 18 +++-- src/App/Pages/Accounts/HomePageViewModel.cs | 3 +- src/App/Pages/Accounts/LockPageViewModel.cs | 11 ++- src/App/Pages/Accounts/LoginPageViewModel.cs | 11 ++- .../Accounts/VerificationCodeViewModel.cs | 27 +++---- .../GeneratorHistoryPageViewModel.cs | 9 +-- src/App/Pages/Send/SendAddEditPage.xaml.cs | 8 +-- .../Pages/Send/SendAddEditPageViewModel.cs | 10 ++- .../Settings/ExportVaultPageViewModel.cs | 17 ++--- src/App/Pages/Vault/AddEditPageViewModel.cs | 23 +++--- .../GroupingsPage/GroupingsPageViewModel.cs | 4 +- src/App/Pages/Vault/ScanPage.xaml.cs | 9 +-- src/App/Utilities/ThemeManager.cs | 16 ++--- src/Core/Abstractions/ILogger.cs | 26 +++++++ .../{ILogService.cs => INativeLogService.cs} | 2 +- src/Core/Core.csproj | 5 ++ src/Core/Services/ConsoleLogService.cs | 2 +- src/Core/Services/Logging/DebugLogger.cs | 50 +++++++++++++ src/Core/Services/Logging/Logger.cs | 71 +++++++++++++++++++ src/Core/Services/Logging/LoggerHelper.cs | 33 +++++++++ src/Core/Services/Logging/StubLogger.cs | 21 ++++++ src/Core/Utilities/TaskExtensions.cs | 8 +-- src/iOS.Core/Utilities/iOSCoreHelpers.cs | 18 ++++- src/iOS.Core/Utilities/iOSHelpers.cs | 4 +- src/iOS/AppDelegate.cs | 2 +- .../Services/iOSPushNotificationHandler.cs | 4 +- 32 files changed, 328 insertions(+), 147 deletions(-) create mode 100644 src/Core/Abstractions/ILogger.cs rename src/Core/Abstractions/{ILogService.cs => INativeLogService.cs} (83%) create mode 100644 src/Core/Services/Logging/DebugLogger.cs create mode 100644 src/Core/Services/Logging/Logger.cs create mode 100644 src/Core/Services/Logging/LoggerHelper.cs create mode 100644 src/Core/Services/Logging/StubLogger.cs diff --git a/src/Android/Autofill/AutofillService.cs b/src/Android/Autofill/AutofillService.cs index c339794bc7..07171eb9ea 100644 --- a/src/Android/Autofill/AutofillService.cs +++ b/src/Android/Autofill/AutofillService.cs @@ -1,4 +1,7 @@ -using Android; +using System; +using System.Collections.Generic; +using System.Linq; +using Android; using Android.App; using Android.Content; using Android.OS; @@ -9,12 +12,6 @@ using Bit.Core; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Utilities; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif -using System; -using System.Collections.Generic; -using System.Linq; namespace Bit.Droid.Autofill { @@ -28,6 +25,7 @@ namespace Bit.Droid.Autofill private IVaultTimeoutService _vaultTimeoutService; private IPolicyService _policyService; private IStateService _stateService; + private LazyResolve _logger = new LazyResolve("logger"); public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) @@ -84,9 +82,7 @@ namespace Bit.Droid.Autofill } catch (Exception e) { -#if !FDROID - Crashes.TrackError(e); -#endif + _logger.Value.Exception(e); } } @@ -160,9 +156,7 @@ namespace Bit.Droid.Autofill } catch (Exception e) { -#if !FDROID - Crashes.TrackError(e); -#endif + _logger.Value.Exception(e); } } } diff --git a/src/Android/MainActivity.cs b/src/Android/MainActivity.cs index c11f8b9f73..4018dd7345 100644 --- a/src/Android/MainActivity.cs +++ b/src/Android/MainActivity.cs @@ -69,7 +69,7 @@ namespace Bit.Droid Window.AddFlags(Android.Views.WindowManagerFlags.Secure); } -#if !FDROID +#if !DEBUG && !FDROID var appCenterHelper = new AppCenterHelper(_appIdService, _stateService); var appCenterTask = appCenterHelper.InitAsync(); #endif diff --git a/src/Android/MainApplication.cs b/src/Android/MainApplication.cs index ff2fd307a8..aadd11564c 100644 --- a/src/Android/MainApplication.cs +++ b/src/Android/MainApplication.cs @@ -53,7 +53,8 @@ namespace Bit.Droid ServiceContainer.Resolve("apiService"), ServiceContainer.Resolve("messagingService"), ServiceContainer.Resolve("platformUtilsService"), - ServiceContainer.Resolve("deviceActionService")); + ServiceContainer.Resolve("deviceActionService"), + ServiceContainer.Resolve("logger")); ServiceContainer.Register("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner); var verificationActionsFlowHelper = new VerificationActionsFlowHelper( @@ -87,7 +88,14 @@ namespace Bit.Droid private void RegisterLocalServices() { - ServiceContainer.Register("logService", new AndroidLogService()); + ServiceContainer.Register("nativeLogService", new AndroidLogService()); +#if FDROID + ServiceContainer.Register("logger", new StubLogger()); +#elif DEBUG + ServiceContainer.Register("logger", DebugLogger.Instance); +#else + ServiceContainer.Register("logger", Logger.Instance); +#endif // Note: This might cause a race condition. Investigate more. Task.Run(() => diff --git a/src/Android/Services/AndroidLogService.cs b/src/Android/Services/AndroidLogService.cs index ead61510d8..8d7cdac54b 100644 --- a/src/Android/Services/AndroidLogService.cs +++ b/src/Android/Services/AndroidLogService.cs @@ -3,7 +3,7 @@ using System; namespace Bit.Core.Services { - public class AndroidLogService : ILogService + public class AndroidLogService : INativeLogService { private static readonly string _tag = "BITWARDEN"; diff --git a/src/App/Controls/AccountSwitchingOverlay/AccountSwitchingOverlayView.xaml.cs b/src/App/Controls/AccountSwitchingOverlay/AccountSwitchingOverlayView.xaml.cs index 8f213dcba2..b4af21f982 100644 --- a/src/App/Controls/AccountSwitchingOverlay/AccountSwitchingOverlayView.xaml.cs +++ b/src/App/Controls/AccountSwitchingOverlay/AccountSwitchingOverlayView.xaml.cs @@ -1,9 +1,8 @@ using System; using System.Threading.Tasks; using System.Windows.Input; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif +using Bit.Core.Abstractions; +using Bit.Core.Utilities; using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.Forms; @@ -23,14 +22,14 @@ namespace Bit.App.Controls set => SetValue(MainFabProperty, value); } + readonly LazyResolve _logger = new LazyResolve("logger"); + public AccountSwitchingOverlayView() { InitializeComponent(); ToggleVisibililtyCommand = new AsyncCommand(ToggleVisibilityAsync, -#if !FDROID - onException: ex => Crashes.TrackError(ex), -#endif + onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false); } @@ -110,9 +109,7 @@ namespace Bit.App.Controls } catch (Exception ex) { -#if !FDROID - Crashes.TrackError(ex); -#endif + _logger.Value.Exception(ex); } } @@ -133,9 +130,7 @@ namespace Bit.App.Controls } catch (Exception ex) { -#if !FDROID - Crashes.TrackError(ex); -#endif + _logger.Value.Exception(ex); } } } diff --git a/src/App/Controls/AccountSwitchingOverlay/AccountSwitchingOverlayViewModel.cs b/src/App/Controls/AccountSwitchingOverlay/AccountSwitchingOverlayViewModel.cs index bc15fe96ad..ed6f521ca5 100644 --- a/src/App/Controls/AccountSwitchingOverlay/AccountSwitchingOverlayViewModel.cs +++ b/src/App/Controls/AccountSwitchingOverlay/AccountSwitchingOverlayViewModel.cs @@ -4,7 +4,6 @@ using System.Windows.Input; using Bit.Core.Abstractions; using Bit.Core.Models.View; using Bit.Core.Utilities; -using Microsoft.AppCenter.Crashes; using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.Forms; @@ -16,15 +15,14 @@ namespace Bit.App.Controls private readonly IMessagingService _messagingService; public AccountSwitchingOverlayViewModel(IStateService stateService, - IMessagingService messagingService) + IMessagingService messagingService, + ILogger logger) { _stateService = stateService; _messagingService = messagingService; SelectAccountCommand = new AsyncCommand(SelectAccountAsync, -#if !FDROID - onException: ex => Crashes.TrackError(ex), -#endif + onException: ex => logger.Exception(ex), allowsMultipleExecutions: false); } diff --git a/src/App/Pages/Accounts/DeleteAccountViewModel.cs b/src/App/Pages/Accounts/DeleteAccountViewModel.cs index cab70bd38c..6705e19499 100644 --- a/src/App/Pages/Accounts/DeleteAccountViewModel.cs +++ b/src/App/Pages/Accounts/DeleteAccountViewModel.cs @@ -5,9 +5,6 @@ using Bit.App.Utilities; using Bit.Core.Abstractions; using Bit.Core.Exceptions; using Bit.Core.Utilities; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif namespace Bit.App.Pages { @@ -15,11 +12,13 @@ namespace Bit.App.Pages { readonly IPlatformUtilsService _platformUtilsService; readonly IVerificationActionsFlowHelper _verificationActionsFlowHelper; + readonly ILogger _logger; public DeleteAccountViewModel() { _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _verificationActionsFlowHelper = ServiceContainer.Resolve("verificationActionsFlowHelper"); + _logger = ServiceContainer.Resolve("logger"); PageTitle = AppResources.DeleteAccount; } @@ -44,9 +43,7 @@ namespace Bit.App.Pages } catch (System.Exception ex) { -#if !FDROID - Crashes.TrackError(ex); -#endif + _logger.Exception(ex); await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred); } } @@ -60,16 +57,19 @@ namespace Bit.App.Pages readonly IMessagingService _messagingService; readonly IPlatformUtilsService _platformUtilsService; readonly IDeviceActionService _deviceActionService; + readonly ILogger _logger; public DeleteAccountActionFlowExecutioner(IApiService apiService, IMessagingService messagingService, IPlatformUtilsService platformUtilsService, - IDeviceActionService deviceActionService) + IDeviceActionService deviceActionService, + ILogger logger) { _apiService = apiService; _messagingService = messagingService; _platformUtilsService = platformUtilsService; _deviceActionService = deviceActionService; + _logger = logger; } public async Task Execute(IActionFlowParmeters parameters) @@ -102,9 +102,7 @@ namespace Bit.App.Pages catch (System.Exception ex) { await _deviceActionService.HideLoadingAsync(); -#if !FDROID - Crashes.TrackError(ex); -#endif + _logger.Exception(ex); await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred); } } diff --git a/src/App/Pages/Accounts/HomePageViewModel.cs b/src/App/Pages/Accounts/HomePageViewModel.cs index d0f9519623..bd44d55677 100644 --- a/src/App/Pages/Accounts/HomePageViewModel.cs +++ b/src/App/Pages/Accounts/HomePageViewModel.cs @@ -17,10 +17,11 @@ namespace Bit.App.Pages { _stateService = ServiceContainer.Resolve("stateService"); _messagingService = ServiceContainer.Resolve("messagingService"); + var logger = ServiceContainer.Resolve("logger"); PageTitle = AppResources.Bitwarden; - AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService) + AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, logger) { AllowActiveAccountSelection = true }; diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index 59ca8c69dc..a4515c01bd 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -11,9 +11,6 @@ using Bit.Core.Models.Domain; using Bit.Core.Models.Request; using Bit.Core.Utilities; using Xamarin.Forms; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif namespace Bit.App.Pages { @@ -29,6 +26,7 @@ namespace Bit.App.Pages private readonly IStateService _stateService; private readonly IBiometricService _biometricService; private readonly IKeyConnectorService _keyConnectorService; + private readonly ILogger _logger; private string _email; private bool _showPassword; @@ -55,12 +53,13 @@ namespace Bit.App.Pages _stateService = ServiceContainer.Resolve("stateService"); _biometricService = ServiceContainer.Resolve("biometricService"); _keyConnectorService = ServiceContainer.Resolve("keyConnectorService"); + _logger = ServiceContainer.Resolve("logger"); PageTitle = AppResources.VerifyMasterPassword; TogglePasswordCommand = new Command(TogglePassword); SubmitCommand = new Command(async () => await SubmitAsync()); - AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService) + AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger) { AllowAddAccountRow = true, AllowActiveAccountSelection = true @@ -151,9 +150,7 @@ namespace Bit.App.Pages if (string.IsNullOrWhiteSpace(_email)) { await _vaultTimeoutService.LogOutAsync(); -#if !FDROID - Crashes.TrackError(new NullReferenceException("Email not found in storage")); -#endif + _logger.Exception(new NullReferenceException("Email not found in storage")); return; } var webVault = _environmentService.GetWebVaultUrl(true); diff --git a/src/App/Pages/Accounts/LoginPageViewModel.cs b/src/App/Pages/Accounts/LoginPageViewModel.cs index 7514e9047c..709d7cc80c 100644 --- a/src/App/Pages/Accounts/LoginPageViewModel.cs +++ b/src/App/Pages/Accounts/LoginPageViewModel.cs @@ -8,9 +8,6 @@ using Bit.Core; using Bit.Core.Abstractions; using Bit.Core.Exceptions; using Bit.Core.Utilities; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif using Xamarin.Forms; namespace Bit.App.Pages @@ -25,6 +22,7 @@ namespace Bit.App.Pages private readonly IEnvironmentService _environmentService; private readonly II18nService _i18nService; private readonly IMessagingService _messagingService; + private readonly ILogger _logger; private bool _showPassword; private bool _showCancelButton; @@ -41,12 +39,13 @@ namespace Bit.App.Pages _environmentService = ServiceContainer.Resolve("environmentService"); _i18nService = ServiceContainer.Resolve("i18nService"); _messagingService = ServiceContainer.Resolve("messagingService"); + _logger = ServiceContainer.Resolve("logger"); PageTitle = AppResources.Bitwarden; TogglePasswordCommand = new Command(TogglePassword); LogInCommand = new Command(async () => await LogInAsync()); - AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService) + AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger) { AllowAddAccountRow = true, AllowActiveAccountSelection = true @@ -221,9 +220,7 @@ namespace Bit.App.Pages } catch (Exception e) { -#if !FDROID - Crashes.TrackError(e); -#endif + _logger.Exception(e); } } diff --git a/src/App/Pages/Accounts/VerificationCodeViewModel.cs b/src/App/Pages/Accounts/VerificationCodeViewModel.cs index e160a535d8..7f1c62bbb2 100644 --- a/src/App/Pages/Accounts/VerificationCodeViewModel.cs +++ b/src/App/Pages/Accounts/VerificationCodeViewModel.cs @@ -1,19 +1,16 @@ using System; +using System.Threading.Tasks; +using System.Windows.Input; using Bit.App.Abstractions; using Bit.App.Resources; -using Bit.Core.Abstractions; -using Bit.Core.Utilities; -using System.Threading.Tasks; -using Bit.Core.Exceptions; -using Xamarin.Forms; -using Xamarin.CommunityToolkit.ObjectModel; -using System.Windows.Input; using Bit.App.Utilities; using Bit.Core; +using Bit.Core.Abstractions; using Bit.Core.Enums; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif +using Bit.Core.Exceptions; +using Bit.Core.Utilities; +using Xamarin.CommunityToolkit.ObjectModel; +using Xamarin.Forms; namespace Bit.App.Pages { @@ -24,6 +21,7 @@ namespace Bit.App.Pages private readonly IUserVerificationService _userVerificationService; private readonly IApiService _apiService; private readonly IVerificationActionsFlowHelper _verificationActionsFlowHelper; + private readonly ILogger _logger; private bool _showPassword; private string _secret, _mainActionText, _sendCodeStatus; @@ -35,6 +33,7 @@ namespace Bit.App.Pages _userVerificationService = ServiceContainer.Resolve("userVerificationService"); _apiService = ServiceContainer.Resolve("apiService"); _verificationActionsFlowHelper = ServiceContainer.Resolve("verificationActionsFlowHelper"); + _logger = ServiceContainer.Resolve("logger"); PageTitle = AppResources.VerificationCode; @@ -118,9 +117,7 @@ namespace Bit.App.Pages } catch (Exception ex) { -#if !FDROID - Crashes.TrackError(ex); -#endif + _logger.Exception(ex); await _deviceActionService.HideLoadingAsync(); SendCodeStatus = AppResources.AnErrorOccurredWhileSendingAVerificationCodeToYourEmailPleaseTryAgain; } @@ -171,9 +168,7 @@ namespace Bit.App.Pages } catch (Exception ex) { -#if !FDROID - Crashes.TrackError(ex); -#endif + _logger.Exception(ex); await _deviceActionService.HideLoadingAsync(); } } diff --git a/src/App/Pages/Generator/GeneratorHistoryPageViewModel.cs b/src/App/Pages/Generator/GeneratorHistoryPageViewModel.cs index e92aedde4a..ab63c81b0a 100644 --- a/src/App/Pages/Generator/GeneratorHistoryPageViewModel.cs +++ b/src/App/Pages/Generator/GeneratorHistoryPageViewModel.cs @@ -4,9 +4,6 @@ using Bit.App.Resources; using Bit.Core.Abstractions; using Bit.Core.Models.Domain; using Bit.Core.Utilities; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif using Xamarin.Forms; namespace Bit.App.Pages @@ -16,6 +13,7 @@ namespace Bit.App.Pages private readonly IPlatformUtilsService _platformUtilsService; private readonly IPasswordGenerationService _passwordGenerationService; private readonly IClipboardService _clipboardService; + private readonly ILogger _logger; private bool _showNoData; @@ -24,6 +22,7 @@ namespace Bit.App.Pages _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _passwordGenerationService = ServiceContainer.Resolve("passwordGenerationService"); _clipboardService = ServiceContainer.Resolve("clipboardService"); + _logger = ServiceContainer.Resolve("logger"); PageTitle = AppResources.PasswordHistory; History = new ExtendedObservableCollection(); @@ -70,9 +69,7 @@ namespace Bit.App.Pages } catch (System.Exception ex) { -#if !FDROID - Crashes.TrackError(ex); -#endif + _logger.Exception(ex); } } } diff --git a/src/App/Pages/Send/SendAddEditPage.xaml.cs b/src/App/Pages/Send/SendAddEditPage.xaml.cs index a0b1aa2d88..3121acf0e9 100644 --- a/src/App/Pages/Send/SendAddEditPage.xaml.cs +++ b/src/App/Pages/Send/SendAddEditPage.xaml.cs @@ -7,9 +7,6 @@ using Bit.App.Utilities; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Utilities; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif using Xamarin.Forms; using Xamarin.Forms.PlatformConfiguration; using Xamarin.Forms.PlatformConfiguration.iOSSpecific; @@ -21,6 +18,7 @@ namespace Bit.App.Pages { private readonly IBroadcasterService _broadcasterService; private readonly IVaultTimeoutService _vaultTimeoutService; + private readonly LazyResolve _logger = new LazyResolve("logger"); private AppOptions _appOptions; private SendAddEditPageViewModel _vm; @@ -132,9 +130,7 @@ namespace Bit.App.Pages } catch (Exception ex) { -#if !FDROID - Crashes.TrackError(ex); -#endif + _logger.Value.Exception(ex); await CloseAsync(); } } diff --git a/src/App/Pages/Send/SendAddEditPageViewModel.cs b/src/App/Pages/Send/SendAddEditPageViewModel.cs index 4a213a1b17..78642e362d 100644 --- a/src/App/Pages/Send/SendAddEditPageViewModel.cs +++ b/src/App/Pages/Send/SendAddEditPageViewModel.cs @@ -10,9 +10,6 @@ using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.View; using Bit.Core.Utilities; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif using Xamarin.Essentials; using Xamarin.Forms; @@ -25,6 +22,7 @@ namespace Bit.App.Pages private readonly IMessagingService _messagingService; private readonly IStateService _stateService; private readonly ISendService _sendService; + private readonly ILogger _logger; private bool _sendEnabled; private bool _canAccessPremium; private bool _emailVerified; @@ -58,6 +56,8 @@ namespace Bit.App.Pages _messagingService = ServiceContainer.Resolve("messagingService"); _stateService = ServiceContainer.Resolve("stateService"); _sendService = ServiceContainer.Resolve("sendService"); + _logger = ServiceContainer.Resolve("logger"); + TogglePasswordCommand = new Command(TogglePassword); TypeOptions = new List> @@ -455,9 +455,7 @@ namespace Bit.App.Pages catch (Exception ex) { await _deviceActionService.HideLoadingAsync(); -#if !FDROID - Crashes.TrackError(ex); -#endif + _logger.Exception(ex); await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred); } return false; diff --git a/src/App/Pages/Settings/ExportVaultPageViewModel.cs b/src/App/Pages/Settings/ExportVaultPageViewModel.cs index 80218b8347..de84447ba3 100644 --- a/src/App/Pages/Settings/ExportVaultPageViewModel.cs +++ b/src/App/Pages/Settings/ExportVaultPageViewModel.cs @@ -1,17 +1,14 @@ using System; -using Bit.App.Abstractions; -using Bit.App.Resources; -using Bit.Core.Abstractions; -using Bit.Core.Utilities; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; +using Bit.App.Abstractions; +using Bit.App.Resources; +using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Exceptions; +using Bit.Core.Utilities; using Bit.Core; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif using Xamarin.Forms; namespace Bit.App.Pages @@ -26,6 +23,7 @@ namespace Bit.App.Pages private readonly IKeyConnectorService _keyConnectorService; private readonly IUserVerificationService _userVerificationService; private readonly IApiService _apiService; + private readonly ILogger _logger; private int _fileFormatSelectedIndex; private string _exportWarningMessage; @@ -48,6 +46,7 @@ namespace Bit.App.Pages _keyConnectorService = ServiceContainer.Resolve("keyConnectorService"); _userVerificationService = ServiceContainer.Resolve("userVerificationService"); _apiService = ServiceContainer.Resolve("apiService"); + _logger = ServiceContainer.Resolve("logger"); PageTitle = AppResources.ExportVault; TogglePasswordCommand = new Command(TogglePassword); @@ -189,9 +188,7 @@ namespace Bit.App.Pages ClearResult(); await _platformUtilsService.ShowDialogAsync(_i18nService.T("ExportVaultFailure")); System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", ex.GetType(), ex.StackTrace); -#if !FDROID - Crashes.TrackError(ex); -#endif + _logger.Exception(ex); } } diff --git a/src/App/Pages/Vault/AddEditPageViewModel.cs b/src/App/Pages/Vault/AddEditPageViewModel.cs index 9ca606a061..f71c7ced9b 100644 --- a/src/App/Pages/Vault/AddEditPageViewModel.cs +++ b/src/App/Pages/Vault/AddEditPageViewModel.cs @@ -1,21 +1,18 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Bit.App.Abstractions; +using Bit.App.Controls; using Bit.App.Models; using Bit.App.Resources; +using Bit.Core; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.View; using Bit.Core.Utilities; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Bit.App.Controls; -using Bit.Core; using Xamarin.Forms; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif namespace Bit.App.Pages { @@ -32,6 +29,8 @@ namespace Bit.App.Pages private readonly IMessagingService _messagingService; private readonly IEventService _eventService; private readonly IPolicyService _policyService; + private readonly ILogger _logger; + private CipherView _cipher; private bool _showNotesSeparator; private bool _showPassword; @@ -68,7 +67,7 @@ namespace Bit.App.Pages new KeyValuePair(UriMatchType.Exact, AppResources.Exact), new KeyValuePair(UriMatchType.Never, AppResources.Never) }; - + public AddEditPageViewModel() { _deviceActionService = ServiceContainer.Resolve("deviceActionService"); @@ -82,6 +81,8 @@ namespace Bit.App.Pages _collectionService = ServiceContainer.Resolve("collectionService"); _eventService = ServiceContainer.Resolve("eventService"); _policyService = ServiceContainer.Resolve("policyService"); + _logger = ServiceContainer.Resolve("logger"); + GeneratePasswordCommand = new Command(GeneratePassword); TogglePasswordCommand = new Command(TogglePassword); ToggleCardNumberCommand = new Command(ToggleCardNumber); @@ -538,9 +539,7 @@ namespace Bit.App.Pages } catch(Exception genex) { -#if !FDROID - Crashes.TrackError(genex); -#endif + _logger.Exception(genex); await _deviceActionService.HideLoadingAsync(); } return false; diff --git a/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs b/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs index 20e4ddafda..941660b375 100644 --- a/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs +++ b/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs @@ -45,6 +45,7 @@ namespace Bit.App.Pages private readonly IMessagingService _messagingService; private readonly IStateService _stateService; private readonly IPasswordRepromptService _passwordRepromptService; + private readonly ILogger _logger; public GroupingsPageViewModel() { @@ -58,6 +59,7 @@ namespace Bit.App.Pages _messagingService = ServiceContainer.Resolve("messagingService"); _stateService = ServiceContainer.Resolve("stateService"); _passwordRepromptService = ServiceContainer.Resolve("passwordRepromptService"); + _logger = ServiceContainer.Resolve("logger"); Loading = true; PageTitle = AppResources.MyVault; @@ -69,7 +71,7 @@ namespace Bit.App.Pages }); CipherOptionsCommand = new Command(CipherOptionsAsync); - AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService) + AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger) { AllowAddAccountRow = true }; diff --git a/src/App/Pages/Vault/ScanPage.xaml.cs b/src/App/Pages/Vault/ScanPage.xaml.cs index 30d6fb09f8..cf7e7a68ea 100644 --- a/src/App/Pages/Vault/ScanPage.xaml.cs +++ b/src/App/Pages/Vault/ScanPage.xaml.cs @@ -2,7 +2,8 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Microsoft.AppCenter.Crashes; +using Bit.Core.Abstractions; +using Bit.Core.Utilities; using Xamarin.Forms; namespace Bit.App.Pages @@ -14,6 +15,8 @@ namespace Bit.App.Pages private CancellationTokenSource _autofocusCts; private Task _continuousAutofocusTask; + private readonly LazyResolve _logger = new LazyResolve("logger"); + public ScanPage(Action callback) { _callback = callback; @@ -61,9 +64,7 @@ namespace Bit.App.Pages catch (TaskCanceledException) { } catch (Exception ex) { -#if !FDROID - Crashes.TrackError(ex); -#endif + _logger.Value.Exception(ex); } }, autofocusCts.Token); } diff --git a/src/App/Utilities/ThemeManager.cs b/src/App/Utilities/ThemeManager.cs index 7bc50788b6..3dabf093dd 100644 --- a/src/App/Utilities/ThemeManager.cs +++ b/src/App/Utilities/ThemeManager.cs @@ -1,14 +1,12 @@ using System; +using System.Linq; +using System.Threading.Tasks; using Bit.App.Models; using Bit.App.Styles; using Bit.Core.Abstractions; +using Bit.Core.Services; using Bit.Core.Utilities; using Xamarin.Forms; -using System.Linq; -using System.Threading.Tasks; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif namespace Bit.App.Utilities { @@ -76,9 +74,7 @@ namespace Bit.App.Utilities } catch (Exception ex) { -#if !FDROID - Crashes.TrackError(ex); -#endif + LoggerHelper.LogEvenIfCantBeResolved(ex); } } @@ -168,9 +164,7 @@ namespace Bit.App.Utilities } catch (Exception ex) { -#if !FDROID - Crashes.TrackError(ex); -#endif + LoggerHelper.LogEvenIfCantBeResolved(ex); } } } diff --git a/src/Core/Abstractions/ILogger.cs b/src/Core/Abstractions/ILogger.cs new file mode 100644 index 0000000000..704fca7583 --- /dev/null +++ b/src/Core/Abstractions/ILogger.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Bit.Core.Abstractions +{ + public interface ILogger + { + /// + /// Logs something that is not in itself an exception, e.g. a wrong flow or value that needs to be reported + /// and looked into. + /// + /// A text to be used as the issue's title + /// Additional data + void Error(string message, + IDictionary extraData = null, + [CallerMemberName] string memberName = "", + [CallerFilePath] string sourceFilePath = "", + [CallerLineNumber] int sourceLineNumber = 0); + + /// + /// Logs an exception + /// + void Exception(Exception ex); + } +} diff --git a/src/Core/Abstractions/ILogService.cs b/src/Core/Abstractions/INativeLogService.cs similarity index 83% rename from src/Core/Abstractions/ILogService.cs rename to src/Core/Abstractions/INativeLogService.cs index 8af1a806a4..fdadcca379 100644 --- a/src/Core/Abstractions/ILogService.cs +++ b/src/Core/Abstractions/INativeLogService.cs @@ -1,6 +1,6 @@ namespace Bit.Core.Abstractions { - public interface ILogService + public interface INativeLogService { void Debug(string message); void Error(string message); diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 09129dec51..6aceb3c571 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -15,6 +15,8 @@ + + @@ -31,4 +33,7 @@ + + + diff --git a/src/Core/Services/ConsoleLogService.cs b/src/Core/Services/ConsoleLogService.cs index baa292f766..4928918346 100644 --- a/src/Core/Services/ConsoleLogService.cs +++ b/src/Core/Services/ConsoleLogService.cs @@ -3,7 +3,7 @@ using System; namespace Bit.Core.Services { - public class ConsoleLogService : ILogService + public class ConsoleLogService : INativeLogService { public void Debug(string message) { diff --git a/src/Core/Services/Logging/DebugLogger.cs b/src/Core/Services/Logging/DebugLogger.cs new file mode 100644 index 0000000000..e70fbda805 --- /dev/null +++ b/src/Core/Services/Logging/DebugLogger.cs @@ -0,0 +1,50 @@ +#if !FDROID +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using Bit.Core.Abstractions; + +namespace Bit.Core.Services +{ + public class DebugLogger : ILogger + { + static ILogger _instance; + public static ILogger Instance + { + get + { + if (_instance is null) + { + _instance = new DebugLogger(); + } + return _instance; + } + } + + protected DebugLogger() + { + } + + public void Error(string message, IDictionary extraData = null, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) + { + var classAndMethod = $"{Path.GetFileNameWithoutExtension(sourceFilePath)}.{memberName}"; + var filePathAndLineNumber = $"{Path.GetFileName(sourceFilePath)}:{sourceLineNumber}"; + + if (string.IsNullOrEmpty(message)) + { + Debug.WriteLine($"Error found in: {classAndMethod})"); + return; + } + + Debug.WriteLine($"File: {filePathAndLineNumber}"); + Debug.WriteLine($"Method: {memberName}"); + Debug.WriteLine($"Message: {message}"); + + } + + public void Exception(Exception ex) => Debug.WriteLine(ex); + } +} +#endif diff --git a/src/Core/Services/Logging/Logger.cs b/src/Core/Services/Logging/Logger.cs new file mode 100644 index 0000000000..69f7814356 --- /dev/null +++ b/src/Core/Services/Logging/Logger.cs @@ -0,0 +1,71 @@ +#if !FDROID +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using Bit.Core.Abstractions; +using Microsoft.AppCenter.Crashes; + +namespace Bit.Core.Services +{ + public class Logger : ILogger + { + static ILogger _instance; + public static ILogger Instance + { + get + { + if (_instance is null) + { + _instance = new Logger(); + } + return _instance; + } + } + + protected Logger() + { + } + + public void Error(string message, + IDictionary extraData = null, + [CallerMemberName] string memberName = "", + [CallerFilePath] string sourceFilePath = "", + [CallerLineNumber] int sourceLineNumber = 0) + { + var classAndMethod = $"{Path.GetFileNameWithoutExtension(sourceFilePath)}.{memberName}"; + var filePathAndLineNumber = $"{Path.GetFileName(sourceFilePath)}:{sourceLineNumber}"; + var properties = new Dictionary + { + ["File"] = filePathAndLineNumber, + ["Method"] = memberName + }; + + var exception = new Exception(message ?? $"Error found in: {classAndMethod}"); + if (extraData == null) + { + Crashes.TrackError(exception, properties); + } + else + { + var data = properties.Concat(extraData).ToDictionary(x => x.Key, x => x.Value); + Crashes.TrackError(exception, data); + } + } + + public void Exception(Exception exception) + { + try + { + Crashes.TrackError(exception); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } + } + } +} +#endif diff --git a/src/Core/Services/Logging/LoggerHelper.cs b/src/Core/Services/Logging/LoggerHelper.cs new file mode 100644 index 0000000000..cfce40af6f --- /dev/null +++ b/src/Core/Services/Logging/LoggerHelper.cs @@ -0,0 +1,33 @@ +using System; +using Bit.Core.Abstractions; +using Bit.Core.Utilities; +#if !FDROID +using Microsoft.AppCenter.Crashes; +#endif + +namespace Bit.Core.Services +{ + public static class LoggerHelper + { + /// + /// Logs the exception even if the service can't be resolved. + /// Useful when we need to log an exception in situations where the ServiceContainer may not be initialized. + /// + /// + public static void LogEvenIfCantBeResolved(Exception ex) + { + if (ServiceContainer.Resolve("logger", true) is ILogger logger) + { + logger.Exception(ex); + } + else + { +#if !FDROID + // just in case the caller throws the exception in a moment where the logger can't be resolved + // we need to track the error as well + Crashes.TrackError(ex); +#endif + } + } + } +} diff --git a/src/Core/Services/Logging/StubLogger.cs b/src/Core/Services/Logging/StubLogger.cs new file mode 100644 index 0000000000..aa3db6affc --- /dev/null +++ b/src/Core/Services/Logging/StubLogger.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Bit.Core.Abstractions; + +namespace Bit.Core.Services +{ + /// + /// A logger that does nothing, this is useful on e.g. FDroid, where we cannot use logging through AppCenter + /// + public class StubLogger : ILogger + { + public void Error(string message, IDictionary extraData = null, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) + { + } + + public void Exception(Exception ex) + { + } + } +} diff --git a/src/Core/Utilities/TaskExtensions.cs b/src/Core/Utilities/TaskExtensions.cs index 8e3eb8a6f0..2400671f65 100644 --- a/src/Core/Utilities/TaskExtensions.cs +++ b/src/Core/Utilities/TaskExtensions.cs @@ -1,8 +1,6 @@ using System; using System.Threading.Tasks; -#if !FDROID -using Microsoft.AppCenter.Crashes; -#endif +using Bit.Core.Services; namespace Bit.Core.Utilities { @@ -22,9 +20,7 @@ namespace Bit.Core.Utilities } catch (Exception ex) { -#if !FDROID - Crashes.TrackError(ex); -#endif + LoggerHelper.LogEvenIfCantBeResolved(ex); onException?.Invoke(ex); } } diff --git a/src/iOS.Core/Utilities/iOSCoreHelpers.cs b/src/iOS.Core/Utilities/iOSCoreHelpers.cs index be8034106d..26190c20b7 100644 --- a/src/iOS.Core/Utilities/iOSCoreHelpers.cs +++ b/src/iOS.Core/Utilities/iOSCoreHelpers.cs @@ -27,17 +27,28 @@ namespace Bit.iOS.Core.Utilities public static void RegisterAppCenter() { +#if !DEBUG var appCenterHelper = new AppCenterHelper( ServiceContainer.Resolve("appIdService"), ServiceContainer.Resolve("stateService")); var appCenterTask = appCenterHelper.InitAsync(); +#endif } public static void RegisterLocalServices() { - if (ServiceContainer.Resolve("logService", true) == null) + if (ServiceContainer.Resolve("nativeLogService", true) == null) { - ServiceContainer.Register("logService", new ConsoleLogService()); + ServiceContainer.Register("nativeLogService", new ConsoleLogService()); + } + + if (ServiceContainer.Resolve("logger", true) == null) + { +#if DEBUG + ServiceContainer.Register("logger", DebugLogger.Instance); +#else + ServiceContainer.Register("logger", Logger.Instance); +#endif } var preferencesStorage = new PreferencesStorageService(AppGroupId); @@ -156,7 +167,8 @@ namespace Bit.iOS.Core.Utilities ServiceContainer.Resolve("apiService"), ServiceContainer.Resolve("messagingService"), ServiceContainer.Resolve("platformUtilsService"), - ServiceContainer.Resolve("deviceActionService")); + ServiceContainer.Resolve("deviceActionService"), + ServiceContainer.Resolve("logger")); ServiceContainer.Register("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner); var verificationActionsFlowHelper = new VerificationActionsFlowHelper( diff --git a/src/iOS.Core/Utilities/iOSHelpers.cs b/src/iOS.Core/Utilities/iOSHelpers.cs index 7bb32057e7..d10cf7403c 100644 --- a/src/iOS.Core/Utilities/iOSHelpers.cs +++ b/src/iOS.Core/Utilities/iOSHelpers.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.InteropServices; using Bit.App.Utilities; -using Microsoft.AppCenter.Crashes; +using Bit.Core.Services; using UIKit; using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; @@ -39,7 +39,7 @@ namespace Bit.iOS.Core.Utilities } catch (Exception e) { - Crashes.TrackError(e); + Logger.Instance.Exception(e); } finally { diff --git a/src/iOS/AppDelegate.cs b/src/iOS/AppDelegate.cs index a426a6a3bf..aeedeec984 100644 --- a/src/iOS/AppDelegate.cs +++ b/src/iOS/AppDelegate.cs @@ -287,7 +287,7 @@ namespace Bit.iOS } // Migration services - ServiceContainer.Register("logService", new ConsoleLogService()); + ServiceContainer.Register("nativeLogService", new ConsoleLogService()); // Note: This might cause a race condition. Investigate more. Task.Run(() => diff --git a/src/iOS/Services/iOSPushNotificationHandler.cs b/src/iOS/Services/iOSPushNotificationHandler.cs index bfa9824323..197a7fe1a2 100644 --- a/src/iOS/Services/iOSPushNotificationHandler.cs +++ b/src/iOS/Services/iOSPushNotificationHandler.cs @@ -1,8 +1,8 @@ using System; using System.Diagnostics; using Bit.App.Abstractions; +using Bit.Core.Services; using Foundation; -using Microsoft.AppCenter.Crashes; using Newtonsoft.Json.Linq; using UserNotifications; using Xamarin.Forms; @@ -45,7 +45,7 @@ namespace Bit.iOS.Services } catch (Exception ex) { - Crashes.TrackError(ex); + Logger.Instance.Exception(ex); } }