PageRenderTime 965ms CodeModel.GetById 32ms RepoModel.GetById 3ms app.codeStats 0ms

/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
  1. using System;
  2. using System.Collections.ObjectModel;
  3. using System.Linq;
  4. using System.Windows;
  5. using VXCompany.SilverlightLogging.MVVM.Models.Stores;
  6. using VXCompany.SilverlightLogging.MVVM.Models.SyncBehaviors;
  7. using VXCompany.SilverlightLogging.ServiceProxy;
  8. namespace VXCompany.SilverlightLogging.MVVM.Models
  9. {
  10. /// <summary>
  11. /// Handles client exceptions. Created by LoggingApplicationService.
  12. /// </summary>
  13. /// <example>
  14. /// To configure logging on WP7, add this code to App.xaml:
  15. /// 1. Add namespaces:
  16. /// xmlns:svc="clr-namespace:VXCompany.SilverlightLogging;assembly=VXCompany.SilverlightLogging.WP7"
  17. /// xmlns:str="clr-namespace:VXCompany.SilverlightLogging.MVVM.Models.Stores;assembly=VXCompany.SilverlightLogging.WP7"
  18. ///
  19. /// 2. Add ApplicationLifetimeObject:
  20. /// <Application.ApplicationLifetimeObjects>
  21. /// <svc:LoggingApplicationService ApplicationName="WP7 Test UI">
  22. /// <svc:LoggingApplicationService.Store>
  23. /// <str:ApplicationStore />
  24. /// </svc:LoggingApplicationService.Store>
  25. /// </svc:LoggingApplicationService>
  26. /// </Application.ApplicationLifetimeObjects>
  27. ///
  28. /// To configure logging on SL4, add this code to App.xaml:
  29. /// 1. Add namespaces:
  30. /// xmlns:svc="clr-namespace:VXCompany.SilverlightLogging;assembly=VXCompany.SilverlightLogging.SL4"
  31. /// xmlns:str="clr-namespace:VXCompany.SilverlightLogging.MVVM.Models.Stores;assembly=VXCompany.SilverlightLogging.SL4"
  32. ///
  33. /// 2. Add ApplicationLifetimeObject:
  34. /// <Application.ApplicationLifetimeObjects>
  35. /// <svc:LoggingApplicationService ApplicationName="SL4 Test UI">
  36. /// <svc:LoggingApplicationService.Store>
  37. /// <str:ApplicationStore />
  38. /// </svc:LoggingApplicationService.Store>
  39. /// </svc:LoggingApplicationService>
  40. /// </Application.ApplicationLifetimeObjects>
  41. ///
  42. /// ApplicationName can be specified, and you can explicitly create a temporary store for Exceptions. Available stores are:
  43. /// 1. ApplicationStore, which uses IsolatedStorageSettings.ApplicationSettings to store errors in.
  44. /// 2. IsoStreamStore, which uses IsolatedStorageFileStream to store errors in.
  45. /// </example>
  46. public class ExceptionPolicy : NotifyingBase
  47. {
  48. #region Static Members
  49. /// <summary>
  50. /// Convenience property that returns LoggingApplicationService.Current.ExceptionPolicy. (provided that the service is added to App.xaml)
  51. /// </summary>
  52. public static ExceptionPolicy Current
  53. {
  54. get
  55. {
  56. if (LoggingApplicationService.Current != null)
  57. {
  58. return LoggingApplicationService.Current.ExceptionPolicy;
  59. }
  60. return null;
  61. }
  62. }
  63. #endregion Static Members
  64. private ServiceHelper _ServiceHelper;
  65. private IStore _Store;
  66. private LoggingServiceClient _LoggingServiceClient;
  67. private string _ApplicationName;
  68. private ObservableCollection<ISyncBehavior> _RunningSyncBehaviors;
  69. private LoggingConfiguration _LoggingConfiguration;
  70. private bool _IsServiceAvailable;
  71. private bool _IsSynchronizing;
  72. #region Properties
  73. /// <summary>
  74. /// Gets the exception store.
  75. /// </summary>
  76. public IStore Store
  77. {
  78. get { return _Store; }
  79. private set
  80. {
  81. _Store = value;
  82. RaisePropertyChanged("Store");
  83. }
  84. }
  85. /// <summary>
  86. /// Gets LoggingServiceClient (using the ILoggingServiceFactory if it is available)
  87. /// </summary>
  88. public LoggingServiceClient LoggingServiceClient
  89. {
  90. get
  91. {
  92. if (LoggingServiceFactory != null)
  93. {
  94. LoggingServiceClient = LoggingServiceFactory.GetProxy();
  95. }
  96. return _LoggingServiceClient;
  97. }
  98. private set
  99. {
  100. if (_LoggingServiceClient == value)
  101. return;
  102. _LoggingServiceClient = value;
  103. RaisePropertyChanged("LoggingServiceClient");
  104. }
  105. }
  106. private ILoggingServiceClientFactory _LoggingServiceFactory;
  107. /// <summary>
  108. /// Gets or sets ILoggingServiceFactory implementation.
  109. /// </summary>
  110. public ILoggingServiceClientFactory LoggingServiceFactory
  111. {
  112. get { return _LoggingServiceFactory; }
  113. set
  114. {
  115. if (value == _LoggingServiceFactory)
  116. return;
  117. _LoggingServiceFactory = value;
  118. RaisePropertyChanged("LoggingServiceFactory");
  119. }
  120. }
  121. /// <summary>
  122. /// Gets the configured logging settings.
  123. /// </summary>
  124. public LoggingConfiguration LoggingConfiguration
  125. {
  126. get { return _LoggingConfiguration; }
  127. private set
  128. {
  129. _LoggingConfiguration = value;
  130. RaisePropertyChanged("LoggingConfiguration");
  131. }
  132. }
  133. /// <summary>
  134. /// Gets or sets the application name.
  135. /// </summary>
  136. public string ApplicationName
  137. {
  138. get { return _ApplicationName; }
  139. set
  140. {
  141. _ApplicationName = value;
  142. RaisePropertyChanged("ApplicationName");
  143. }
  144. }
  145. /// <summary>
  146. /// Gets all SyncBehaviors.
  147. /// </summary>
  148. public ObservableCollection<ISyncBehavior> RunningSyncBehaviors
  149. {
  150. get { return _RunningSyncBehaviors; }
  151. private set
  152. {
  153. _RunningSyncBehaviors = value;
  154. RaisePropertyChanged("RunningSyncBehaviors");
  155. }
  156. }
  157. /// <summary>
  158. /// Indicates whether service is available.
  159. /// </summary>
  160. public bool IsServiceAvailable
  161. {
  162. get { return _IsServiceAvailable; }
  163. private set
  164. {
  165. _IsServiceAvailable = value;
  166. RaisePropertyChanged("IsServiceAvailable");
  167. }
  168. }
  169. /// <summary>
  170. /// Indicates whether a synchronization attempt is being made.
  171. /// </summary>
  172. public bool IsSynchronizing
  173. {
  174. get { return _IsSynchronizing; }
  175. private set
  176. {
  177. _IsSynchronizing = value;
  178. RaisePropertyChanged("IsSynchronizing");
  179. }
  180. }
  181. #endregion Properties
  182. /// <summary>
  183. /// Creates new instance, with default Store.
  184. /// </summary>
  185. internal ExceptionPolicy()
  186. {
  187. //init using default store:
  188. IStore store = new ApplicationStore();
  189. TryInitializeStore(ref store);
  190. //catch unhandled errors
  191. Application.Current.UnhandledException += new EventHandler<ApplicationUnhandledExceptionEventArgs>(Current_UnhandledException);
  192. }
  193. /// <summary>
  194. /// Initializes logging policy Store with configured Store.
  195. /// </summary>
  196. public bool TryInitializeStore(ref IStore localStore)
  197. {
  198. bool isStoreOK = true;
  199. if (localStore == null)
  200. {
  201. throw new ArgumentNullException("localStore");
  202. }
  203. // trying to configure a store that doesn't work
  204. if (!localStore.IsStorageAvailable)
  205. {
  206. //log to memory as a fallback scenario.
  207. localStore = new MemoryStore();
  208. isStoreOK = false;
  209. }
  210. else
  211. {
  212. //transfer existing errors when switching Stores.
  213. if (Store != null
  214. && Store.GetType() != localStore.GetType()
  215. && Store.IsStorageAvailable
  216. )
  217. {
  218. foreach (var item in Store)
  219. {
  220. localStore.Add(item);
  221. }
  222. Store.Clear();
  223. }
  224. }
  225. Store = localStore;
  226. //notify admin
  227. if (Store is MemoryStore)
  228. {
  229. HandleException(new Exception("Using memory store at client."), ErrorLevel.Warning);
  230. }
  231. return isStoreOK;
  232. }
  233. /// <summary>
  234. /// Initialzes logging service client, using provided client, or the factory, or the configuration file (if both client and factory are null).
  235. /// </summary>
  236. /// <param name="client"></param>
  237. public void InitializeServiceClient(LoggingServiceClient client = null)
  238. {
  239. if (LoggingServiceFactory == null)
  240. {
  241. LoggingServiceClient = new LoggingServiceClient();
  242. }
  243. _ServiceHelper = new ServiceHelper(this);
  244. _ServiceHelper.ConfigurationUpdated += (sender, ea) =>
  245. {
  246. this.LoggingConfiguration = ea.Configuration;
  247. SetSyncBehavior();
  248. };
  249. _ServiceHelper.BeginGetConfig();
  250. _ServiceHelper.PropertyChanged += (sender, ea) =>
  251. {
  252. if (ea.PropertyName == "IsServiceAvailable")
  253. {
  254. IsServiceAvailable = _ServiceHelper.IsServiceAvailable;
  255. }
  256. else if (ea.PropertyName == "IsSynchronizing")
  257. {
  258. IsSynchronizing = _ServiceHelper.IsSynchronizing;
  259. }
  260. };
  261. }
  262. /// <summary>
  263. /// Handles the provided client exception.
  264. /// </summary>
  265. /// <remarks>To be logged, the exception must be provided with a level higher than or equal to the MinLogLevel in the configuration.</remarks>
  266. /// <param name="exception"></param>
  267. public void HandleException(Exception exception, ErrorLevel level = ErrorLevel.Critical)
  268. {
  269. if (Store != null
  270. && exception != null
  271. && level >= LoggingConfiguration.MinLogLevel)
  272. {
  273. var exceptionMessage = MapExceptionToError(exception, level);
  274. Store.Add(exceptionMessage);
  275. }
  276. }
  277. /// <summary>
  278. /// Handles unhandled exceptions.
  279. /// </summary>
  280. /// <param name="sender"></param>
  281. /// <param name="e"></param>
  282. private void Current_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
  283. {
  284. HandleException(e.ExceptionObject, ErrorLevel.Critical);
  285. }
  286. /// <summary>
  287. /// Sets Sync behavior based on configuration.
  288. /// </summary>
  289. private void SetSyncBehavior()
  290. {
  291. if (RunningSyncBehaviors == null)
  292. {
  293. RunningSyncBehaviors = new ObservableCollection<ISyncBehavior>();
  294. }
  295. else
  296. {
  297. if (RunningSyncBehaviors.Count > 0)
  298. {
  299. RunningSyncBehaviors.Cast<IInternalSyncBehavior>().ForEach(b => b.Stop());
  300. RunningSyncBehaviors.Clear();
  301. }
  302. }
  303. if (SyncBehaviorFlags.AppCrash == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.AppCrash))
  304. {
  305. RunningSyncBehaviors.Add(new SyncBehaviors.AppCrashSync());
  306. }
  307. if (SyncBehaviorFlags.AppStart == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.AppStart))
  308. {
  309. RunningSyncBehaviors.Add(new SyncBehaviors.AppStartSync());
  310. }
  311. if (SyncBehaviorFlags.AppEnd == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.AppEnd))
  312. {
  313. RunningSyncBehaviors.Add(new SyncBehaviors.AppEndSync());
  314. }
  315. if (SyncBehaviorFlags.PerMinute == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.PerMinute))
  316. {
  317. RunningSyncBehaviors.Add(new SyncBehaviors.PeriodicSync(TimeSpan.FromMinutes(1)));
  318. }
  319. if (SyncBehaviorFlags.PerTwoMinutes == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.PerTwoMinutes))
  320. {
  321. RunningSyncBehaviors.Add(new SyncBehaviors.PeriodicSync(TimeSpan.FromMinutes(2)));
  322. }
  323. if (SyncBehaviorFlags.PerFiveMinutes == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.PerFiveMinutes))
  324. {
  325. RunningSyncBehaviors.Add(new SyncBehaviors.PeriodicSync(TimeSpan.FromMinutes(5)));
  326. }
  327. if (SyncBehaviorFlags.PerTenMinutes == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.PerTenMinutes))
  328. {
  329. RunningSyncBehaviors.Add(new SyncBehaviors.PeriodicSync(TimeSpan.FromMinutes(10)));
  330. }
  331. if (SyncBehaviorFlags.PerFifteenMinutes == (LoggingConfiguration.SyncBehaviorFlags & SyncBehaviorFlags.PerFifteenMinutes))
  332. {
  333. RunningSyncBehaviors.Add(new SyncBehaviors.PeriodicSync(TimeSpan.FromMinutes(15)));
  334. }
  335. ////To test failing behavior, enable the line below.
  336. //RunningSyncBehaviors.Add(new TestSvcFailureBehavior());
  337. //Configure behaviors.
  338. ConfigureSyncBehaviors();
  339. }
  340. /// <summary>
  341. /// Configures all behaviors in RunningSyncBehaviors.
  342. /// </summary>
  343. private void ConfigureSyncBehaviors()
  344. {
  345. foreach (var item in RunningSyncBehaviors.Cast<IInternalSyncBehavior>())
  346. {
  347. _ServiceHelper.ExceptionsSynced += (sender, ea) =>
  348. {
  349. EndSynchronizeAll(ea.Result);
  350. };
  351. item.SyncAction = () => { if (_ServiceHelper.IsServiceAvailable) _ServiceHelper.BeginWrite(); };
  352. item.Start();
  353. }
  354. }
  355. /// <summary>
  356. /// Calls EndSynchronize on all active sync items.
  357. /// </summary>
  358. /// <param name="result"></param>
  359. private void EndSynchronizeAll(Result result)
  360. {
  361. foreach (var item in RunningSyncBehaviors.Cast<IInternalSyncBehavior>())
  362. {
  363. if (item.IsSynchronizing)
  364. {
  365. item.EndSynchronize(result);
  366. }
  367. }
  368. }
  369. /// <summary>
  370. /// Maps Exception to ExceptionMessage.
  371. /// </summary>
  372. /// <param name="exception"></param>
  373. /// <param name="level"></param>
  374. /// <returns></returns>
  375. private ExceptionMessage MapExceptionToError(Exception exception, ErrorLevel level = ErrorLevel.Critical)
  376. {
  377. string exceptionString = exception.ToString();
  378. return new ExceptionMessage
  379. {
  380. Message = exceptionString,
  381. Level = level,
  382. ClientApplication = ApplicationName,
  383. ClientAddress = LoggingConfiguration != null ? LoggingConfiguration.ClientAddress : "unknown address",
  384. UserID = LoggingConfiguration != null ? LoggingConfiguration.UserID : "unknown user",
  385. ExceptionMessageID = Guid.NewGuid(),
  386. DateTime = DateTime.Now.ToUniversalTime(),
  387. InnerException = exception.InnerException != null ? MapExceptionToError(exception.InnerException) : null,
  388. };
  389. }
  390. }
  391. }