Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for offline storage of errors to Raygun4Net Core #530

Merged
merged 21 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a25db7c
Add support for offline storage of errors to Raygun4Net Core
xenolightning May 14, 2024
1fddfd9
Add overload to ctor
xenolightning May 14, 2024
61d25e8
Fix the serialization issues and restructure the sending of payloads
xenolightning May 14, 2024
9f09a71
Bit of a rename and removal of sent reports working with location
xenolightning May 15, 2024
d0b6f7f
Store a map of locations for removing cache entries
xenolightning May 15, 2024
e1b697e
Fix tests
xenolightning May 15, 2024
781916a
naming
xenolightning May 15, 2024
0e6ddd1
No cast!
xenolightning May 28, 2024
f34d373
Employ a finally block to clean up the logic
xenolightning May 28, 2024
5b7358c
Move the crash store into a getter/setter property instead of the ctor
xenolightning May 28, 2024
4c4dbda
Rename to store
xenolightning May 29, 2024
9f4b667
Move store to settings, and clean up send logic
xenolightning May 29, 2024
610d347
Flip the processing to the store, move the sending into a strategy.
xenolightning May 29, 2024
4986a57
Make interfaces explicit
xenolightning May 30, 2024
22fea6a
Not everything needs to be an interface!
xenolightning May 30, 2024
cba4c80
Correct some of the sending logic.
xenolightning May 30, 2024
690c705
Add boolean to prevent re-saving failed offline errors
xenolightning May 30, 2024
7a8d205
Attempt to fix the env var tests
xenolightning May 30, 2024
1ce8541
Reset the last updated on each test
xenolightning May 30, 2024
a7940e0
Add nullables
xenolightning Jun 3, 2024
0074dec
pr feedbacks
xenolightning Jun 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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; }
}
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 Mindscape.Raygun4Net.NetCore.Common/Offline/OfflineStoreBase.cs
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;
xenolightning marked this conversation as resolved.
Show resolved Hide resolved
}

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);
}
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();
}
}
Loading