/Development/VXCompany.SilverlightLogging/VXCompany.SilverlightLogging.WP7/MVVM/Models/ExceptionPolicy.cs
C# | 427 lines | 283 code | 36 blank | 108 comment | 53 complexity | 4e6a4ef4649965b88747eaafbda186c3 MD5 | raw file
- using System;
- using System.Collections.ObjectModel;
- using System.Linq;
- using System.Windows;
- using VXCompany.SilverlightLogging.MVVM.Models.Stores;
- using VXCompany.SilverlightLogging.MVVM.Models.SyncBehaviors;
- using VXCompany.SilverlightLogging.ServiceProxy;
- namespace VXCompany.SilverlightLogging.MVVM.Models
- {
- /// <summary>
- /// Handles client exceptions. Created by LoggingApplicationService.
- /// </summary>
- /// <example>
- /// To configure logging on WP7, add this code to App.xaml:
- /// 1. Add namespaces:
- /// xmlns:svc="clr-namespace:VXCompany.SilverlightLogging;assembly=VXCompany.SilverlightLogging.WP7"
- /// xmlns:str="clr-namespace:VXCompany.SilverlightLogging.MVVM.Models.Stores;assembly=VXCompany.SilverlightLogging.WP7"
- ///
- /// 2. Add ApplicationLifetimeObject:
- /// <Application.ApplicationLifetimeObjects>
- /// <svc:LoggingApplicationService ApplicationName="WP7 Test UI">
- /// <svc:LoggingApplicationService.Store>
- /// <str:ApplicationStore />
- /// </svc:LoggingApplicationService.Store>
- /// </svc:LoggingApplicationService>
- /// </Application.ApplicationLifetimeObjects>
- ///
- /// To configure logging on SL4, add this code to App.xaml:
- /// 1. Add namespaces:
- /// xmlns:svc="clr-namespace:VXCompany.SilverlightLogging;assembly=VXCompany.SilverlightLogging.SL4"
- /// xmlns:str="clr-namespace:VXCompany.SilverlightLogging.MVVM.Models.Stores;assembly=VXCompany.SilverlightLogging.SL4"
- ///
- /// 2. Add ApplicationLifetimeObject:
- /// <Application.ApplicationLifetimeObjects>
- /// <svc:LoggingApplicationService ApplicationName="SL4 Test UI">
- /// <svc:LoggingApplicationService.Store>
- /// <str:ApplicationStore />
- /// </svc:LoggingApplicationService.Store>
- /// </svc:LoggingApplicationService>
- /// </Application.ApplicationLifetimeObjects>
- ///
- /// ApplicationName can be specified, and you can explicitly create a temporary store for Exceptions. Available stores are:
- /// 1. ApplicationStore, which uses IsolatedStorageSettings.ApplicationSettings to store errors in.
- /// 2. IsoStreamStore, which uses IsolatedStorageFileStream to store errors in.
- /// </example>
- public class ExceptionPolicy : NotifyingBase
- {
- #region Static Members
-
- /// <summary>
- /// Convenience property that returns LoggingApplicationService.Current.ExceptionPolicy. (provided that the service is added to App.xaml)
- /// </summary>
- public static ExceptionPolicy Current
- {
- get
- {
- if (LoggingApplicationService.Current != null)
- {
- return LoggingApplicationService.Current.ExceptionPolicy;
- }
- return null;
- }
- }
-
- #endregion Static Members
-
- private ServiceHelper _ServiceHelper;
- private IStore _Store;
- private LoggingServiceClient _LoggingServiceClient;
- private string _ApplicationName;
- private ObservableCollection<ISyncBehavior> _RunningSyncBehaviors;
- private LoggingConfiguration _LoggingConfiguration;
- private bool _IsServiceAvailable;
- private bool _IsSynchronizing;
-
- #region Properties
-
- /// <summary>
- /// Gets the exception store.
- /// </summary>
- public IStore Store
- {
- get { return _Store; }
- private set
- {
- _Store = value;
- RaisePropertyChanged("Store");
- }
- }
-
- /// <summary>
- /// Gets LoggingServiceClient (using the ILoggingServiceFactory if it is available)
- /// </summary>
- public LoggingServiceClient LoggingServiceClient
- {
- get
- {
- if (LoggingServiceFactory != null)
- {
- LoggingServiceClient = LoggingServiceFactory.GetProxy();
- }
- return _LoggingServiceClient;
- }
- private set
- {
- if (_LoggingServiceClient == value)
- return;
-
- _LoggingServiceClient = value;
- RaisePropertyChanged("LoggingServiceClient");
- }
- }
-
- private ILoggingServiceClientFactory _LoggingServiceFactory;
- /// <summary>
- /// Gets or sets ILoggingServiceFactory implementation.
- /// </summary>
- public ILoggingServiceClientFactory LoggingServiceFactory
- {
- get { return _LoggingServiceFactory; }
- set
- {
- if (value == _LoggingServiceFactory)
- return;
-
- _LoggingServiceFactory = value;
- RaisePropertyChanged("LoggingServiceFactory");
- }
- }
-
-
- /// <summary>
- /// Gets the configured logging settings.
- /// </summary>
- public LoggingConfiguration LoggingConfiguration
- {
- get { return _LoggingConfiguration; }
- private set
- {
- _LoggingConfiguration = value;
- RaisePropertyChanged("LoggingConfiguration");
- }
- }
-
- /// <summary>
- /// Gets or sets the application name.
- /// </summary>
- public string ApplicationName
- {
- get { return _ApplicationName; }
- set
- {
- _ApplicationName = value;
- RaisePropertyChanged("ApplicationName");
- }
- }
-
- /// <summary>
- /// Gets all SyncBehaviors.
- /// </summary>
- public ObservableCollection<ISyncBehavior> RunningSyncBehaviors
- {
- get { return _RunningSyncBehaviors; }
- private set
- {
- _RunningSyncBehaviors = value;
- RaisePropertyChanged("RunningSyncBehaviors");
- }
- }
-
- /// <summary>
- /// Indicates whether service is available.
- /// </summary>
- public bool IsServiceAvailable
- {
- get { return _IsServiceAvailable; }
- private set
- {
- _IsServiceAvailable = value;
- RaisePropertyChanged("IsServiceAvailable");
- }
- }
-
-
- /// <summary>
- /// Indicates whether a synchronization attempt is being made.
- /// </summary>
- public bool IsSynchronizing
- {
- get { return _IsSynchronizing; }
- private set
- {
- _IsSynchronizing = value;
- RaisePropertyChanged("IsSynchronizing");
- }
- }
-
- #endregion Properties
-
- /// <summary>
- /// Creates new instance, with default Store.
- /// </summary>
- internal ExceptionPolicy()
- {
- //init using default store:
- IStore store = new ApplicationStore();
- TryInitializeStore(ref store);
- //catch unhandled errors
- Application.Current.UnhandledException += new EventHandler<ApplicationUnhandledExceptionEventArgs>(Current_UnhandledException);
- }
-
- /// <summary>
- /// Initializes logging policy Store with configured Store.
- /// </summary>
- public bool TryInitializeStore(ref IStore localStore)
- {
- bool isStoreOK = true;
- if (localStore == null)
- {
- throw new ArgumentNullException("localStore");
- }
-
- // trying to configure a store that doesn't work
- if (!localStore.IsStorageAvailable)
- {
- //log to memory as a fallback scenario.
- localStore = new MemoryStore();
- isStoreOK = false;
- }
- else
- {
- //transfer existing errors when switching Stores.
- if (Store != null
- && Store.GetType() != localStore.GetType()
- && Store.IsStorageAvailable
- )
- {
- foreach (var item in Store)
- {
- localStore.Add(item);
- }
- Store.Clear();
- }
- }
-
- Store = localStore;
-
- //notify admin
- if (Store is MemoryStore)
- {
- HandleException(new Exception("Using memory store at client."), ErrorLevel.Warning);
- }
-
- return isStoreOK;
- }
-
- /// <summary>
- /// Initialzes logging service client, using provided client, or the factory, or the configuration file (if both client and factory are null).
- /// </summary>
- /// <param name="client"></param>
- public void InitializeServiceClient(LoggingServiceClient client = null)
- {
- if (LoggingServiceFactory == null)
- {
- LoggingServiceClient = new LoggingServiceClient();
- }
-
- _ServiceHelper = new ServiceHelper(this);
- _ServiceHelper.ConfigurationUpdated += (sender, ea) =>
- {
- this.LoggingConfiguration = ea.Configuration;
- SetSyncBehavior();
- };
- _ServiceHelper.BeginGetConfig();
-
- _ServiceHelper.PropertyChanged += (sender, ea) =>
- {
- if (ea.PropertyName == "IsServiceAvailable")
- {
- IsServiceAvailable = _ServiceHelper.IsServiceAvailable;
- }
- else if (ea.PropertyName == "IsSynchronizing")
- {
- IsSynchronizing = _ServiceHelper.IsSynchronizing;
- }
- };
- }
-
- /// <summary>
- /// Handles the provided client exception.
- /// </summary>
- /// <remarks>To be logged, the exception must be provided with a level higher than or equal to the MinLogLevel in the configuration.</remarks>
- /// <param name="exception"></param>
- public void HandleException(Exception exception, ErrorLevel level = ErrorLevel.Critical)
- {
- if (Store != null
- && exception != null
- && level >= LoggingConfiguration.MinLogLevel)
- {
- var exceptionMessage = MapExceptionToError(exception, level);
- Store.Add(exceptionMessage);
- }
- }
-
- /// <summary>
- /// Handles unhandled exceptions.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void Current_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
- {
- HandleException(e.ExceptionObject, ErrorLevel.Critical);
- }
-
- /// <summary>
- /// Sets Sync behavior based on configuration.
- /// </summary>
- private void SetSyncBehavior()
- {
- if (RunningSyncBehaviors == null)
- {
- RunningSyncBehaviors = new ObservableCollection<ISyncBehavior>();
- }
- else
- {
- if (RunningSyncBehaviors.Count > 0)
- {
- RunningSyncBehaviors.Cast<IInternalSyncBehavior>().ForEach(b => b.Stop());
- RunningSyncBehaviors.Clear();
- }
- }
-
- if (SyncBehaviorFlags.AppCrash == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.AppCrash))
- {
- RunningSyncBehaviors.Add(new SyncBehaviors.AppCrashSync());
- }
- if (SyncBehaviorFlags.AppStart == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.AppStart))
- {
- RunningSyncBehaviors.Add(new SyncBehaviors.AppStartSync());
- }
- if (SyncBehaviorFlags.AppEnd == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.AppEnd))
- {
- RunningSyncBehaviors.Add(new SyncBehaviors.AppEndSync());
- }
- if (SyncBehaviorFlags.PerMinute == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.PerMinute))
- {
- RunningSyncBehaviors.Add(new SyncBehaviors.PeriodicSync(TimeSpan.FromMinutes(1)));
- }
- if (SyncBehaviorFlags.PerTwoMinutes == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.PerTwoMinutes))
- {
- RunningSyncBehaviors.Add(new SyncBehaviors.PeriodicSync(TimeSpan.FromMinutes(2)));
- }
- if (SyncBehaviorFlags.PerFiveMinutes == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.PerFiveMinutes))
- {
- RunningSyncBehaviors.Add(new SyncBehaviors.PeriodicSync(TimeSpan.FromMinutes(5)));
- }
- if (SyncBehaviorFlags.PerTenMinutes == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.PerTenMinutes))
- {
- RunningSyncBehaviors.Add(new SyncBehaviors.PeriodicSync(TimeSpan.FromMinutes(10)));
- }
- if (SyncBehaviorFlags.PerFifteenMinutes == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.PerFifteenMinutes))
- {
- RunningSyncBehaviors.Add(new SyncBehaviors.PeriodicSync(TimeSpan.FromMinutes(15)));
- }
-
- ////To test failing behavior, enable the line below.
- //RunningSyncBehaviors.Add(new TestSvcFailureBehavior());
-
- //Configure behaviors.
- ConfigureSyncBehaviors();
- }
-
- /// <summary>
- /// Configures all behaviors in RunningSyncBehaviors.
- /// </summary>
- private void ConfigureSyncBehaviors()
- {
- foreach (var item in RunningSyncBehaviors.Cast<IInternalSyncBehavior>())
- {
- _ServiceHelper.ExceptionsSynced += (sender, ea) =>
- {
- EndSynchronizeAll(ea.Result);
- };
- item.SyncAction = () => { if (_ServiceHelper.IsServiceAvailable) _ServiceHelper.BeginWrite(); };
- item.Start();
- }
- }
-
- /// <summary>
- /// Calls EndSynchronize on all active sync items.
- /// </summary>
- /// <param name="result"></param>
- private void EndSynchronizeAll(Result result)
- {
- foreach (var item in RunningSyncBehaviors.Cast<IInternalSyncBehavior>())
- {
- if (item.IsSynchronizing)
- {
- item.EndSynchronize(result);
- }
- }
- }
-
- /// <summary>
- /// Maps Exception to ExceptionMessage.
- /// </summary>
- /// <param name="exception"></param>
- /// <param name="level"></param>
- /// <returns></returns>
- private ExceptionMessage MapExceptionToError(Exception exception, ErrorLevel level = ErrorLevel.Critical)
- {
- string exceptionString = exception.ToString();
-
- return new ExceptionMessage
- {
- Message = exceptionString,
- Level = level,
- ClientApplication = ApplicationName,
- ClientAddress = LoggingConfiguration != null ? LoggingConfiguration.ClientAddress : "unknown address",
- UserID = LoggingConfiguration != null ? LoggingConfiguration.UserID : "unknown user",
- ExceptionMessageID = Guid.NewGuid(),
- DateTime = DateTime.Now.ToUniversalTime(),
- InnerException = exception.InnerException != null ? MapExceptionToError(exception.InnerException) : null,
- };
- }
- }
- }