-
Notifications
You must be signed in to change notification settings - Fork 91
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #530 from MindscapeHQ/sean/offline-storage
Add support for offline storage of errors to Raygun4Net Core
- Loading branch information
Showing
11 changed files
with
474 additions
and
94 deletions.
There are no files selected for viewing
21 changes: 21 additions & 0 deletions
21
Mindscape.Raygun4Net.NetCore.Common/Offline/CrashReportStoreEntry.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using System; | ||
|
||
namespace Mindscape.Raygun4Net.Offline; | ||
|
||
public sealed class CrashReportStoreEntry | ||
{ | ||
/// <summary> | ||
/// Unique ID for the record | ||
/// </summary> | ||
public Guid Id { get; set; } | ||
|
||
/// <summary> | ||
/// The application api key that the payload was intended for | ||
/// </summary> | ||
public string ApiKey { get; set; } | ||
|
||
/// <summary> | ||
/// The JSON serialized payload of the error | ||
/// </summary> | ||
public string MessagePayload { get; set; } | ||
} |
11 changes: 11 additions & 0 deletions
11
Mindscape.Raygun4Net.NetCore.Common/Offline/IBackgroundSendStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace Mindscape.Raygun4Net.Offline; | ||
|
||
public interface IBackgroundSendStrategy : IDisposable | ||
{ | ||
public event Func<Task> OnSendAsync; | ||
public void Start(); | ||
public void Stop(); | ||
} |
61 changes: 61 additions & 0 deletions
61
Mindscape.Raygun4Net.NetCore.Common/Offline/OfflineStoreBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
#nullable enable | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Mindscape.Raygun4Net.Offline; | ||
|
||
public delegate Task SendHandler(string messagePayload, string apiKey, CancellationToken cancellationToken); | ||
|
||
public abstract class OfflineStoreBase | ||
{ | ||
private readonly IBackgroundSendStrategy _backgroundSendStrategy; | ||
protected SendHandler? SendCallback { get; set; } | ||
|
||
protected OfflineStoreBase(IBackgroundSendStrategy backgroundSendStrategy) | ||
{ | ||
_backgroundSendStrategy = backgroundSendStrategy ?? throw new ArgumentNullException(nameof(backgroundSendStrategy)); | ||
_backgroundSendStrategy.OnSendAsync += ProcessOfflineCrashReports; | ||
} | ||
|
||
public virtual void SetSendCallback(SendHandler sendHandler) | ||
{ | ||
SendCallback = sendHandler; | ||
} | ||
|
||
protected virtual async Task ProcessOfflineCrashReports() | ||
{ | ||
if (SendCallback is null) | ||
{ | ||
return; | ||
} | ||
|
||
try | ||
{ | ||
var cachedCrashReports = await GetAll(CancellationToken.None); | ||
foreach (var crashReport in cachedCrashReports) | ||
{ | ||
try | ||
{ | ||
await SendCallback(crashReport.MessagePayload, crashReport.ApiKey, CancellationToken.None); | ||
await Remove(crashReport.Id, CancellationToken.None); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Debug.WriteLine($"Exception sending offline error [{crashReport.Id}]: {ex}"); | ||
throw; | ||
} | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
Debug.WriteLine($"Exception sending offline errors: {ex}"); | ||
} | ||
} | ||
|
||
public abstract Task<List<CrashReportStoreEntry>> GetAll(CancellationToken cancellationToken); | ||
public abstract Task<bool> Save(string crashPayload, string apiKey, CancellationToken cancellationToken); | ||
public abstract Task<bool> Remove(Guid cacheEntryId, CancellationToken cancellationToken); | ||
} |
62 changes: 62 additions & 0 deletions
62
Mindscape.Raygun4Net.NetCore.Common/Offline/TimerBasedSendStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Mindscape.Raygun4Net.Offline; | ||
|
||
public class TimerBasedSendStrategy : IBackgroundSendStrategy | ||
{ | ||
private static readonly TimeSpan DefaultInternal = TimeSpan.FromSeconds(30); | ||
|
||
private readonly Timer _backgroundTimer; | ||
public event Func<Task> OnSendAsync; | ||
|
||
public TimeSpan Interval { get; } | ||
|
||
public TimerBasedSendStrategy(TimeSpan? interval = null) | ||
{ | ||
Interval = interval ?? DefaultInternal; | ||
_backgroundTimer = new Timer(SendOfflineErrors); | ||
Start(); | ||
} | ||
|
||
~TimerBasedSendStrategy() | ||
{ | ||
Dispose(); | ||
} | ||
|
||
private async void SendOfflineErrors(object state) | ||
{ | ||
try | ||
{ | ||
var invocationList = OnSendAsync?.GetInvocationList(); | ||
if (invocationList != null) | ||
{ | ||
var tasks = invocationList.OfType<Func<Task>>().Select(handler => handler()); | ||
await Task.WhenAll(tasks); | ||
} | ||
} | ||
finally | ||
{ | ||
Start(); | ||
} | ||
} | ||
|
||
public void Start() | ||
{ | ||
// This sets the timer to trigger once at the interval, and then "never again". | ||
// This inherently prevents the timer from being re-entrant | ||
_backgroundTimer.Change(Interval, TimeSpan.FromMilliseconds(int.MaxValue)); | ||
} | ||
|
||
public void Stop() | ||
{ | ||
_backgroundTimer.Change(Timeout.Infinite, 0); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_backgroundTimer?.Dispose(); | ||
} | ||
} |
Oops, something went wrong.