diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 95bbe00242..ca24bbac1d 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -21,7 +21,7 @@
-
+
diff --git a/src/Exceptionless.Core/Billing/StripeEventHandler.cs b/src/Exceptionless.Core/Billing/StripeEventHandler.cs
index 7d51e94c79..01dd40d2b7 100644
--- a/src/Exceptionless.Core/Billing/StripeEventHandler.cs
+++ b/src/Exceptionless.Core/Billing/StripeEventHandler.cs
@@ -153,6 +153,12 @@ private async Task InvoicePaymentSucceededAsync(Invoice invoice)
return;
}
+ if (String.IsNullOrEmpty(org.BillingChangedByUserId))
+ {
+ _logger.LogError("No billing user set for organization: {OrganizationId}", org.Id);
+ return;
+ }
+
var user = await _userRepository.GetByIdAsync(org.BillingChangedByUserId);
if (user is null)
{
@@ -172,6 +178,12 @@ private async Task InvoicePaymentFailedAsync(Invoice invoice)
return;
}
+ if (String.IsNullOrEmpty(org.BillingChangedByUserId))
+ {
+ _logger.LogError("No billing user set for organization: {OrganizationId}", org.Id);
+ return;
+ }
+
var user = await _userRepository.GetByIdAsync(org.BillingChangedByUserId);
if (user is null)
{
diff --git a/src/Exceptionless.Core/Configuration/CacheOptions.cs b/src/Exceptionless.Core/Configuration/CacheOptions.cs
index a06c7c778b..3b4a75e76c 100644
--- a/src/Exceptionless.Core/Configuration/CacheOptions.cs
+++ b/src/Exceptionless.Core/Configuration/CacheOptions.cs
@@ -8,7 +8,7 @@ public class CacheOptions
{
public string? ConnectionString { get; internal set; }
public string? Provider { get; internal set; }
- public Dictionary Data { get; internal set; } = null!;
+ public Dictionary Data { get; internal set; } = null!;
public string Scope { get; internal set; } = null!;
public string ScopePrefix { get; internal set; } = null!;
@@ -26,7 +26,7 @@ public static CacheOptions ReadFromConfiguration(IConfiguration config, AppOptio
string? providerConnectionString = !String.IsNullOrEmpty(options.Provider) ? config.GetConnectionString(options.Provider) : null;
var providerOptions = providerConnectionString.ParseConnectionString(defaultKey: "server");
- options.Data ??= new Dictionary(StringComparer.OrdinalIgnoreCase);
+ options.Data ??= new Dictionary(StringComparer.OrdinalIgnoreCase);
options.Data.AddRange(providerOptions);
options.ConnectionString = options.Data.BuildConnectionString(new HashSet { nameof(options.Provider) });
diff --git a/src/Exceptionless.Core/Configuration/MessageBusOptions.cs b/src/Exceptionless.Core/Configuration/MessageBusOptions.cs
index 88a2d019e3..aedb18d935 100644
--- a/src/Exceptionless.Core/Configuration/MessageBusOptions.cs
+++ b/src/Exceptionless.Core/Configuration/MessageBusOptions.cs
@@ -8,7 +8,7 @@ public class MessageBusOptions
{
public string? ConnectionString { get; internal set; }
public string? Provider { get; internal set; }
- public Dictionary Data { get; internal set; } = null!;
+ public Dictionary Data { get; internal set; } = null!;
public string Scope { get; internal set; } = null!;
public string ScopePrefix { get; internal set; } = null!;
@@ -29,7 +29,7 @@ public static MessageBusOptions ReadFromConfiguration(IConfiguration config, App
string? providerConnectionString = !String.IsNullOrEmpty(options.Provider) ? config.GetConnectionString(options.Provider) : null;
var providerOptions = providerConnectionString.ParseConnectionString(defaultKey: "server");
- options.Data ??= new Dictionary(StringComparer.OrdinalIgnoreCase);
+ options.Data ??= new Dictionary(StringComparer.OrdinalIgnoreCase);
options.Data.AddRange(providerOptions);
options.ConnectionString = options.Data.BuildConnectionString(new HashSet { nameof(options.Provider) });
diff --git a/src/Exceptionless.Core/Configuration/MetricOptions.cs b/src/Exceptionless.Core/Configuration/MetricOptions.cs
index 730ad17358..0087982776 100644
--- a/src/Exceptionless.Core/Configuration/MetricOptions.cs
+++ b/src/Exceptionless.Core/Configuration/MetricOptions.cs
@@ -8,7 +8,7 @@ public class MetricOptions
{
public string? ConnectionString { get; internal set; }
public string? Provider { get; internal set; }
- public Dictionary Data { get; internal set; } = null!;
+ public Dictionary Data { get; internal set; } = null!;
public static MetricOptions ReadFromConfiguration(IConfiguration config)
{
diff --git a/src/Exceptionless.Core/Configuration/QueueOptions.cs b/src/Exceptionless.Core/Configuration/QueueOptions.cs
index e8463f6651..62c1a9e4a3 100644
--- a/src/Exceptionless.Core/Configuration/QueueOptions.cs
+++ b/src/Exceptionless.Core/Configuration/QueueOptions.cs
@@ -8,7 +8,7 @@ public class QueueOptions
{
public string? ConnectionString { get; internal set; }
public string? Provider { get; internal set; }
- public Dictionary Data { get; internal set; } = new(StringComparer.OrdinalIgnoreCase);
+ public Dictionary Data { get; internal set; } = new(StringComparer.OrdinalIgnoreCase);
public string Scope { get; internal set; } = null!;
public string ScopePrefix { get; internal set; } = null!;
diff --git a/src/Exceptionless.Core/Configuration/StorageOptions.cs b/src/Exceptionless.Core/Configuration/StorageOptions.cs
index a6d28b8d27..085a459c47 100644
--- a/src/Exceptionless.Core/Configuration/StorageOptions.cs
+++ b/src/Exceptionless.Core/Configuration/StorageOptions.cs
@@ -8,7 +8,7 @@ public class StorageOptions
{
public string? ConnectionString { get; internal set; }
public string? Provider { get; internal set; }
- public Dictionary Data { get; internal set; } = new(StringComparer.OrdinalIgnoreCase);
+ public Dictionary Data { get; internal set; } = new(StringComparer.OrdinalIgnoreCase);
public string Scope { get; internal set; } = null!;
public string ScopePrefix { get; internal set; } = null!;
diff --git a/src/Exceptionless.Core/Exceptionless.Core.csproj b/src/Exceptionless.Core/Exceptionless.Core.csproj
index 66dd25afc1..c93f1b24d4 100644
--- a/src/Exceptionless.Core/Exceptionless.Core.csproj
+++ b/src/Exceptionless.Core/Exceptionless.Core.csproj
@@ -22,19 +22,19 @@
-
-
+
+
-
-
-
+
+
+
-
+
-
+
diff --git a/src/Exceptionless.Core/Extensions/DictionaryExtensions.cs b/src/Exceptionless.Core/Extensions/DictionaryExtensions.cs
index d973b3840b..b78e5ca5f9 100644
--- a/src/Exceptionless.Core/Extensions/DictionaryExtensions.cs
+++ b/src/Exceptionless.Core/Extensions/DictionaryExtensions.cs
@@ -141,12 +141,12 @@ public static int GetCollectionHashCode(this IDictionary
return hashCode;
}
- public static T? GetValueOrDefault(this IDictionary source, string key, T? defaultValue = default)
+ public static T? GetValueOrDefault(this IDictionary source, string key, T? defaultValue = default)
{
if (!source.ContainsKey(key))
return defaultValue;
- object data = source[key];
+ object? data = source[key];
if (data is T variable)
return variable;
@@ -162,12 +162,12 @@ public static int GetCollectionHashCode(this IDictionary
return defaultValue;
}
- public static string GetString(this IDictionary source, string name)
+ public static string GetString(this IDictionary source, string name)
{
return source.GetString(name, String.Empty);
}
- public static string GetString(this IDictionary source, string name, string @default)
+ public static string GetString(this IDictionary source, string name, string @default)
{
if (!source.TryGetValue(name, out string? value) || value is null)
return @default;
diff --git a/src/Exceptionless.Core/Extensions/EnumerableExtensions.cs b/src/Exceptionless.Core/Extensions/EnumerableExtensions.cs
index 89d6d295ce..51ee21bce3 100644
--- a/src/Exceptionless.Core/Extensions/EnumerableExtensions.cs
+++ b/src/Exceptionless.Core/Extensions/EnumerableExtensions.cs
@@ -7,7 +7,7 @@ public static class EnumerableExtensions
{
public static IReadOnlyCollection UnionOriginalAndModified(this IReadOnlyCollection> documents) where T : class, new()
{
- return documents.Select(d => d.Value).Union(documents.Select(d => d.Original).Where(d => d is not null)).ToList();
+ return documents.Select(d => d.Value).Union(documents.Select(d => d.Original).OfType()).ToList();
}
public static bool Contains(this IEnumerable enumerable, Func function)
diff --git a/src/Exceptionless.Core/Jobs/CleanupDataJob.cs b/src/Exceptionless.Core/Jobs/CleanupDataJob.cs
index 23e592c1ca..0218352aec 100644
--- a/src/Exceptionless.Core/Jobs/CleanupDataJob.cs
+++ b/src/Exceptionless.Core/Jobs/CleanupDataJob.cs
@@ -61,9 +61,9 @@ ILoggerFactory loggerFactory
_cacheClient = cacheClient;
}
- protected override Task GetLockAsync(CancellationToken cancellationToken = default)
+ protected override Task GetLockAsync(CancellationToken cancellationToken = default)
{
- return _lockProvider.AcquireAsync(nameof(CleanupDataJob), TimeSpan.FromMinutes(15), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(nameof(CleanupDataJob), TimeSpan.FromMinutes(15), cancellationToken);
}
protected override async Task RunInternalAsync(JobContext context)
@@ -91,7 +91,12 @@ private async Task MarkTokensSuspended(JobContext context)
do
{
- long updatedCount = await _tokenRepository.PatchAllAsync(q => q.Organization(suspendedOrganizations.Hits.Select(o => o.Id)).FieldEquals(t => t.IsSuspended, false), new PartialPatch(new { is_suspended = true }));
+ // Foundatio Hits can contain null elements, so filter them before accessing properties.
+ var suspendedOrganizationIds = suspendedOrganizations.Hits
+ .Where(o => o is not null && o.Id is not null)
+ .Select(o => o!.Id!)
+ .ToList();
+ long updatedCount = await _tokenRepository.PatchAllAsync(q => q.Organization(suspendedOrganizationIds).FieldEquals(t => t.IsSuspended, false), new PartialPatch(new { is_suspended = true }));
if (updatedCount > 0)
_logger.LogInformation("Marking {SuspendedTokenCount} tokens as suspended", updatedCount);
} while (!context.CancellationToken.IsCancellationRequested && await suspendedOrganizations.NextPageAsync());
diff --git a/src/Exceptionless.Core/Jobs/CleanupOrphanedDataJob.cs b/src/Exceptionless.Core/Jobs/CleanupOrphanedDataJob.cs
index 5e09702161..795342dae8 100644
--- a/src/Exceptionless.Core/Jobs/CleanupOrphanedDataJob.cs
+++ b/src/Exceptionless.Core/Jobs/CleanupOrphanedDataJob.cs
@@ -42,9 +42,9 @@ ILoggerFactory loggerFactory
_lockProvider = lockProvider;
}
- protected override Task GetLockAsync(CancellationToken cancellationToken = default)
+ protected override Task GetLockAsync(CancellationToken cancellationToken = default)
{
- return _lockProvider.AcquireAsync(nameof(CleanupOrphanedDataJob), TimeSpan.FromHours(2), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(nameof(CleanupOrphanedDataJob), TimeSpan.FromHours(2), cancellationToken);
}
protected override async Task RunInternalAsync(JobContext context)
diff --git a/src/Exceptionless.Core/Jobs/CloseInactiveSessionsJob.cs b/src/Exceptionless.Core/Jobs/CloseInactiveSessionsJob.cs
index c968f02623..73c50df988 100644
--- a/src/Exceptionless.Core/Jobs/CloseInactiveSessionsJob.cs
+++ b/src/Exceptionless.Core/Jobs/CloseInactiveSessionsJob.cs
@@ -36,9 +36,9 @@ ILoggerFactory loggerFactory
_jsonOptions = jsonOptions;
}
- protected override Task GetLockAsync(CancellationToken cancellationToken = default)
+ protected override Task GetLockAsync(CancellationToken cancellationToken = default)
{
- return _lockProvider.AcquireAsync(nameof(CloseInactiveSessionsJob), TimeSpan.FromMinutes(15), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(nameof(CloseInactiveSessionsJob), TimeSpan.FromMinutes(15), cancellationToken);
}
protected override async Task RunInternalAsync(JobContext context)
diff --git a/src/Exceptionless.Core/Jobs/DailySummaryJob.cs b/src/Exceptionless.Core/Jobs/DailySummaryJob.cs
index 8ccdd02eab..530ae6e2d7 100644
--- a/src/Exceptionless.Core/Jobs/DailySummaryJob.cs
+++ b/src/Exceptionless.Core/Jobs/DailySummaryJob.cs
@@ -51,9 +51,9 @@ ILoggerFactory loggerFactory
_lockProvider = new ThrottlingLockProvider(cacheClient, 1, TimeSpan.FromHours(1), timeProvider, resiliencePolicyProvider, loggerFactory);
}
- protected override Task GetLockAsync(CancellationToken cancellationToken = default)
+ protected override Task GetLockAsync(CancellationToken cancellationToken = default)
{
- return _lockProvider.AcquireAsync(nameof(DailySummaryJob), TimeSpan.FromHours(1), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(nameof(DailySummaryJob), TimeSpan.FromHours(1), cancellationToken);
}
protected override async Task RunInternalAsync(JobContext context)
diff --git a/src/Exceptionless.Core/Jobs/DownloadGeoIPDatabaseJob.cs b/src/Exceptionless.Core/Jobs/DownloadGeoIPDatabaseJob.cs
index 08d8001194..18a93b0bdb 100644
--- a/src/Exceptionless.Core/Jobs/DownloadGeoIPDatabaseJob.cs
+++ b/src/Exceptionless.Core/Jobs/DownloadGeoIPDatabaseJob.cs
@@ -30,9 +30,9 @@ ILoggerFactory loggerFactory
_lockProvider = new ThrottlingLockProvider(cacheClient, 1, TimeSpan.FromDays(1), timeProvider, resiliencePolicyProvider, loggerFactory);
}
- protected override Task GetLockAsync(CancellationToken cancellationToken = default)
+ protected override Task GetLockAsync(CancellationToken cancellationToken = default)
{
- return _lockProvider.AcquireAsync(nameof(DownloadGeoIPDatabaseJob), TimeSpan.FromHours(2), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(nameof(DownloadGeoIPDatabaseJob), TimeSpan.FromHours(2), cancellationToken);
}
protected override async Task RunInternalAsync(JobContext context)
diff --git a/src/Exceptionless.Core/Jobs/Elastic/DataMigrationJob.cs b/src/Exceptionless.Core/Jobs/Elastic/DataMigrationJob.cs
index e3938e2785..41a670598d 100644
--- a/src/Exceptionless.Core/Jobs/Elastic/DataMigrationJob.cs
+++ b/src/Exceptionless.Core/Jobs/Elastic/DataMigrationJob.cs
@@ -141,7 +141,7 @@ protected override async Task RunInternalAsync(JobContext context)
var status = taskStatus?.Task?.Status;
if (taskStatus?.Task is null || status is null)
{
- _logger.LogWarning(taskStatus?.OriginalException, "Error getting task status for {TargetIndex} {TaskId}: {Message}", workItem.TargetIndex, workItem.TaskId, taskStatus.GetErrorMessage());
+ _logger.LogWarning(taskStatus?.OriginalException, "Error getting task status for {TargetIndex} {TaskId}: {Message}", workItem.TargetIndex, workItem.TaskId, taskStatus?.GetErrorMessage());
if (taskStatus?.ServerError?.Status == 429)
await Task.Delay(TimeSpan.FromSeconds(1), _timeProvider);
diff --git a/src/Exceptionless.Core/Jobs/EventNotificationsJob.cs b/src/Exceptionless.Core/Jobs/EventNotificationsJob.cs
index e51f0f0a27..0cc28b6a1f 100644
--- a/src/Exceptionless.Core/Jobs/EventNotificationsJob.cs
+++ b/src/Exceptionless.Core/Jobs/EventNotificationsJob.cs
@@ -60,7 +60,8 @@ public EventNotificationsJob(IQueue queue,
protected override async Task ProcessQueueEntryAsync(QueueEntryContext context)
{
- var wi = context.QueueEntry.Value;
+ var wi = context.QueueEntry.Value!;
+
var ev = await _eventRepository.GetByIdAsync(wi.EventId);
if (ev is null)
return JobResult.SuccessWithMessage($"Could not load event: {wi.EventId}");
diff --git a/src/Exceptionless.Core/Jobs/EventPostsJob.cs b/src/Exceptionless.Core/Jobs/EventPostsJob.cs
index 605ee3ee6c..1807c3d6ba 100644
--- a/src/Exceptionless.Core/Jobs/EventPostsJob.cs
+++ b/src/Exceptionless.Core/Jobs/EventPostsJob.cs
@@ -5,13 +5,13 @@
using Exceptionless.Core.Plugins.EventParser;
using Exceptionless.Core.Queues.Models;
using Exceptionless.Core.Repositories;
-using Foundatio.Repositories.Exceptions;
using Exceptionless.Core.Services;
using Exceptionless.Core.Validation;
using FluentValidation;
using Foundatio.Jobs;
using Foundatio.Queues;
using Foundatio.Repositories;
+using Foundatio.Repositories.Exceptions;
using Foundatio.Resilience;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
@@ -54,10 +54,11 @@ public EventPostsJob(IQueue queue, EventPostService eventPostService,
protected override async Task ProcessQueueEntryAsync(QueueEntryContext context)
{
var entry = context.QueueEntry;
- var ep = entry.Value;
+ var ep = entry.Value!;
+
using var _ = _logger.BeginScope(new ExceptionlessState().Organization(ep.OrganizationId).Project(ep.ProjectId));
- string payloadPath = Path.ChangeExtension(entry.Value.FilePath, ".payload");
+ string payloadPath = Path.ChangeExtension(ep.FilePath, ".payload");
var payloadTask = AppDiagnostics.PostsMarkFileActiveTime.TimeAsync(() => _eventPostService.GetEventPostPayloadAsync(payloadPath));
var projectTask = _projectRepository.GetByIdAsync(ep.ProjectId, o => o.Cache());
var organizationTask = _organizationRepository.GetByIdAsync(ep.OrganizationId, o => o.Cache());
@@ -124,7 +125,10 @@ protected override async Task ProcessQueueEntryAsync(QueueEntryContex
if (uncompressedData.Length > maxEventPostSize)
{
var org = await organizationTask;
- await _usageService.IncrementTooBigAsync(org.Id, project.Id);
+ if (org is not null)
+ await _usageService.IncrementTooBigAsync(org.Id, project.Id);
+ else
+ _logger.LogWarning("Organization {OrganizationId} not found, skipping too-big usage increment for event post {EventPostId}", ep.OrganizationId, entry.Id);
await CompleteEntryAsync(entry, ep, _timeProvider.GetUtcNow().UtcDateTime);
return JobResult.FailedWithMessage($"Unable to process decompressed EventPost data '{payloadPath}' ({payload.Length} bytes compressed, {uncompressedData.Length} bytes): Maximum uncompressed event post size limit ({maxEventPostSize} bytes) reached.");
}
@@ -322,7 +326,7 @@ await _eventPostService.EnqueueAsync(new EventPost(false)
if (!isInternalProject && _logger.IsEnabled(LogLevel.Critical))
{
using (_logger.BeginScope(new ExceptionlessState().Property("Event", new { ev.Date, ev.StackId, ev.Type, ev.Source, ev.Message, ev.Value, ev.Geo, ev.ReferenceId, ev.Tags })))
- _logger.LogCritical(ex, "Error while requeuing event post {FilePath}: {Message}", queueEntry.Value.FilePath, ex.Message);
+ _logger.LogCritical(ex, "Error while requeuing event post {QueueEntryId} {FilePath}: {Message}", queueEntry.Id, queueEntry.Value!.FilePath, ex.Message);
}
AppDiagnostics.EventsRetryErrors.Add(1);
@@ -335,12 +339,12 @@ private Task AbandonEntryAsync(IQueueEntry queueEntry)
return AppDiagnostics.PostsAbandonTime.TimeAsync(queueEntry.AbandonAsync);
}
- private Task CompleteEntryAsync(IQueueEntry entry, EventPostInfo eventPostInfo, DateTime created)
+ private Task CompleteEntryAsync(IQueueEntry entry, EventPost eventPost, DateTime created)
{
return AppDiagnostics.PostsCompleteTime.TimeAsync(async () =>
{
await entry.CompleteAsync();
- await _eventPostService.CompleteEventPostAsync(entry.Value.FilePath, eventPostInfo.ProjectId, created, entry.Value.ShouldArchive);
+ await _eventPostService.CompleteEventPostAsync(eventPost.FilePath, eventPost.ProjectId, created, eventPost.ShouldArchive);
});
}
diff --git a/src/Exceptionless.Core/Jobs/EventUsageJob.cs b/src/Exceptionless.Core/Jobs/EventUsageJob.cs
index b324778206..99c1cff0f2 100644
--- a/src/Exceptionless.Core/Jobs/EventUsageJob.cs
+++ b/src/Exceptionless.Core/Jobs/EventUsageJob.cs
@@ -24,9 +24,9 @@ ILoggerFactory loggerFactory
_lockProvider = lockProvider;
}
- protected override Task GetLockAsync(CancellationToken cancellationToken = default)
+ protected override Task GetLockAsync(CancellationToken cancellationToken = default)
{
- return _lockProvider.AcquireAsync(nameof(EventUsageJob), TimeSpan.FromMinutes(4), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(nameof(EventUsageJob), TimeSpan.FromMinutes(4), cancellationToken);
}
protected override async Task RunInternalAsync(JobContext context)
diff --git a/src/Exceptionless.Core/Jobs/EventUserDescriptionsJob.cs b/src/Exceptionless.Core/Jobs/EventUserDescriptionsJob.cs
index 8b4514b6b3..fc8a5949ad 100644
--- a/src/Exceptionless.Core/Jobs/EventUserDescriptionsJob.cs
+++ b/src/Exceptionless.Core/Jobs/EventUserDescriptionsJob.cs
@@ -1,9 +1,10 @@
-using Exceptionless.Core.Models.Data;
+using Exceptionless.Core.Models;
+using Exceptionless.Core.Models.Data;
using Exceptionless.Core.Queues.Models;
using Exceptionless.Core.Repositories;
-using Foundatio.Repositories.Exceptions;
using Foundatio.Jobs;
using Foundatio.Queues;
+using Foundatio.Repositories.Exceptions;
using Foundatio.Repositories.Extensions;
using Foundatio.Resilience;
using Microsoft.Extensions.Logging;
@@ -23,11 +24,13 @@ public EventUserDescriptionsJob(IQueue queue, IEventReposi
protected override async Task ProcessQueueEntryAsync(QueueEntryContext context)
{
+ var description = context.QueueEntry.Value!;
+
_logger.LogTrace("Processing user description: id={0}", context.QueueEntry.Id);
try
{
- await ProcessUserDescriptionAsync(context.QueueEntry.Value);
+ await ProcessUserDescriptionAsync(description);
_logger.LogInformation("Processed user description: id={Id}", context.QueueEntry.Id);
}
catch (DocumentNotFoundException ex)
@@ -57,7 +60,10 @@ private async Task ProcessUserDescriptionAsync(EventUserDescription description)
};
if (description.Data is not null && description.Data.Count > 0)
+ {
+ ev.Data ??= new DataDictionary();
ev.Data.AddRange(description.Data);
+ }
ev.SetUserDescription(ud);
diff --git a/src/Exceptionless.Core/Jobs/MailMessageJob.cs b/src/Exceptionless.Core/Jobs/MailMessageJob.cs
index bc142a08c8..afbd055c3f 100644
--- a/src/Exceptionless.Core/Jobs/MailMessageJob.cs
+++ b/src/Exceptionless.Core/Jobs/MailMessageJob.cs
@@ -20,12 +20,14 @@ public MailMessageJob(IQueue queue, IMailSender mailSender, TimePro
protected override async Task ProcessQueueEntryAsync(QueueEntryContext context)
{
+ var message = context.QueueEntry.Value!;
+
_logger.LogTrace("Processing message {Id}", context.QueueEntry.Id);
try
{
- await _mailSender.SendAsync(context.QueueEntry.Value);
- _logger.LogInformation("Sent message: to={To} subject={Subject}", context.QueueEntry.Value.To, context.QueueEntry.Value.Subject);
+ await _mailSender.SendAsync(message);
+ _logger.LogInformation("Sent message: to={To} subject={Subject}", message.To, message.Subject);
}
catch (Exception ex)
{
diff --git a/src/Exceptionless.Core/Jobs/StackEventCountJob.cs b/src/Exceptionless.Core/Jobs/StackEventCountJob.cs
index 61fc3721ad..24bbbcd812 100644
--- a/src/Exceptionless.Core/Jobs/StackEventCountJob.cs
+++ b/src/Exceptionless.Core/Jobs/StackEventCountJob.cs
@@ -25,9 +25,9 @@ ILoggerFactory loggerFactory
_lockProvider = new ThrottlingLockProvider(cacheClient, 1, TimeSpan.FromSeconds(5), timeProvider, resiliencePolicyProvider, loggerFactory);
}
- protected override Task GetLockAsync(CancellationToken cancellationToken = default)
+ protected override Task GetLockAsync(CancellationToken cancellationToken = default)
{
- return _lockProvider.AcquireAsync(nameof(StackEventCountJob), TimeSpan.FromSeconds(5), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(nameof(StackEventCountJob), TimeSpan.FromSeconds(5), cancellationToken);
}
protected override async Task RunInternalAsync(JobContext context)
diff --git a/src/Exceptionless.Core/Jobs/StackStatusJob.cs b/src/Exceptionless.Core/Jobs/StackStatusJob.cs
index 9a1ef8897b..5462944a0f 100644
--- a/src/Exceptionless.Core/Jobs/StackStatusJob.cs
+++ b/src/Exceptionless.Core/Jobs/StackStatusJob.cs
@@ -27,9 +27,9 @@ ILoggerFactory loggerFactory
_lockProvider = new ThrottlingLockProvider(cacheClient, 1, TimeSpan.FromSeconds(10), timeProvider, resiliencePolicyProvider, loggerFactory);
}
- protected override Task GetLockAsync(CancellationToken cancellationToken = default)
+ protected override Task GetLockAsync(CancellationToken cancellationToken = default)
{
- return _lockProvider.AcquireAsync(nameof(StackStatusJob), TimeSpan.FromSeconds(10), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(nameof(StackStatusJob), TimeSpan.FromSeconds(10), cancellationToken);
}
protected override async Task RunInternalAsync(JobContext context)
diff --git a/src/Exceptionless.Core/Jobs/WebHooksJob.cs b/src/Exceptionless.Core/Jobs/WebHooksJob.cs
index 9f616db157..65d84f49d8 100644
--- a/src/Exceptionless.Core/Jobs/WebHooksJob.cs
+++ b/src/Exceptionless.Core/Jobs/WebHooksJob.cs
@@ -55,7 +55,8 @@ public WebHooksJob(IQueue queue, IProjectRepository project
protected override async Task ProcessQueueEntryAsync(QueueEntryContext context)
{
- var body = context.QueueEntry.Value;
+ var body = context.QueueEntry.Value!;
+
bool shouldLog = body.ProjectId != _appOptions.InternalProjectId;
using (_logger.BeginScope(new ExceptionlessState().Organization(body.OrganizationId).Project(body.ProjectId)))
{
@@ -161,6 +162,12 @@ private async Task IsEnabledAsync(WebHookNotification body)
switch (body.Type)
{
case WebHookType.General:
+ if (body.WebHookId is null)
+ {
+ _logger.LogWarning("WebHook notification is missing the web hook id. Organization: {OrganizationId}, Project: {ProjectId}, Url: {Url}", body.OrganizationId, body.ProjectId, body.Url);
+ return false;
+ }
+
var webHook = await _webHookRepository.GetByIdAsync(body.WebHookId, o => o.Cache());
return webHook?.IsEnabled ?? false;
case WebHookType.Slack:
diff --git a/src/Exceptionless.Core/Jobs/WorkItemHandlers/FixStackStatsWorkItemHandler.cs b/src/Exceptionless.Core/Jobs/WorkItemHandlers/FixStackStatsWorkItemHandler.cs
index a40ad6c746..30fc8af6fc 100644
--- a/src/Exceptionless.Core/Jobs/WorkItemHandlers/FixStackStatsWorkItemHandler.cs
+++ b/src/Exceptionless.Core/Jobs/WorkItemHandlers/FixStackStatsWorkItemHandler.cs
@@ -26,14 +26,15 @@ public FixStackStatsWorkItemHandler(IStackRepository stackRepository, IEventRepo
_timeProvider = timeProvider;
}
- public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = default)
+ public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = default)
{
return _lockProvider.AcquireAsync(nameof(FixStackStatsWorkItemHandler), TimeSpan.FromHours(1), cancellationToken);
}
public override async Task HandleItemAsync(WorkItemContext context)
{
- var wi = context.GetData();
+ var wi = context.GetData()!;
+
var utcEnd = wi.UtcEnd ?? _timeProvider.GetUtcNow().UtcDateTime;
Log.LogInformation("Starting stack stats repair for {UtcStart:O} to {UtcEnd:O}. OrganizationId={Organization}", wi.UtcStart, utcEnd, wi.OrganizationId);
diff --git a/src/Exceptionless.Core/Jobs/WorkItemHandlers/OrganizationMaintenanceWorkItemHandler.cs b/src/Exceptionless.Core/Jobs/WorkItemHandlers/OrganizationMaintenanceWorkItemHandler.cs
index 30d95134e1..e4d93665d6 100644
--- a/src/Exceptionless.Core/Jobs/WorkItemHandlers/OrganizationMaintenanceWorkItemHandler.cs
+++ b/src/Exceptionless.Core/Jobs/WorkItemHandlers/OrganizationMaintenanceWorkItemHandler.cs
@@ -24,7 +24,7 @@ public OrganizationMaintenanceWorkItemHandler(IOrganizationRepository organizati
_lockProvider = lockProvider;
}
- public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = new())
+ public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = default)
{
return _lockProvider.AcquireAsync(nameof(OrganizationMaintenanceWorkItemHandler), TimeSpan.FromMinutes(15), cancellationToken);
}
@@ -32,7 +32,8 @@ public OrganizationMaintenanceWorkItemHandler(IOrganizationRepository organizati
public override async Task HandleItemAsync(WorkItemContext context)
{
const int LIMIT = 100;
- var wi = context.GetData();
+ var wi = context.GetData()!;
+
Log.LogInformation("Received upgrade organizations work item. Upgrade Plans: {UpgradePlans}", wi.UpgradePlans);
var results = await _organizationRepository.GetAllAsync(o => o.PageLimit(LIMIT));
diff --git a/src/Exceptionless.Core/Jobs/WorkItemHandlers/OrganizationNotificationWorkItemHandler.cs b/src/Exceptionless.Core/Jobs/WorkItemHandlers/OrganizationNotificationWorkItemHandler.cs
index 7542b7f134..840f2bad38 100644
--- a/src/Exceptionless.Core/Jobs/WorkItemHandlers/OrganizationNotificationWorkItemHandler.cs
+++ b/src/Exceptionless.Core/Jobs/WorkItemHandlers/OrganizationNotificationWorkItemHandler.cs
@@ -61,7 +61,8 @@ public OrganizationNotificationWorkItemHandler(IOrganizationRepository organizat
public override Task HandleItemAsync(WorkItemContext context)
{
- var wi = context.GetData();
+ var wi = context.GetData()!;
+
string cacheKey = $"{nameof(OrganizationNotificationWorkItemHandler)}:{wi.OrganizationId}";
return _lockProvider.TryUsingAsync(cacheKey, async () =>
@@ -73,7 +74,7 @@ public override Task HandleItemAsync(WorkItemContext context)
if (wi.IsOverMonthlyLimit)
await SendOverageNotificationsAsync(organization, wi.IsOverHourlyLimit, wi.IsOverMonthlyLimit);
- }, TimeSpan.FromMinutes(15), new CancellationToken(true));
+ }, TimeSpan.FromMinutes(15), context.CancellationToken);
}
private async Task SendOverageNotificationsAsync(Organization organization, bool isOverHourlyLimit, bool isOverMonthlyLimit)
diff --git a/src/Exceptionless.Core/Jobs/WorkItemHandlers/ProjectMaintenanceWorkItemHandler.cs b/src/Exceptionless.Core/Jobs/WorkItemHandlers/ProjectMaintenanceWorkItemHandler.cs
index 129e715c09..f00c98d601 100644
--- a/src/Exceptionless.Core/Jobs/WorkItemHandlers/ProjectMaintenanceWorkItemHandler.cs
+++ b/src/Exceptionless.Core/Jobs/WorkItemHandlers/ProjectMaintenanceWorkItemHandler.cs
@@ -22,16 +22,17 @@ public ProjectMaintenanceWorkItemHandler(IProjectRepository projectRepository, I
_lockProvider = lockProvider;
}
- public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = new())
+ public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = default)
{
- return _lockProvider.AcquireAsync(nameof(ProjectMaintenanceWorkItemHandler), TimeSpan.FromMinutes(15), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(nameof(ProjectMaintenanceWorkItemHandler), TimeSpan.FromMinutes(15), cancellationToken);
}
public override async Task HandleItemAsync(WorkItemContext context)
{
const int LIMIT = 100;
- var workItem = context.GetData();
+ var workItem = context.GetData()!;
+
Log.LogInformation("Received upgrade projects work item. Update Default Bot List: {UpdateDefaultBotList} IncrementConfigurationVersion: {IncrementConfigurationVersion}", workItem.UpdateDefaultBotList, workItem.IncrementConfigurationVersion);
var results = await _projectRepository.GetAllAsync(o => o.PageLimit(LIMIT));
diff --git a/src/Exceptionless.Core/Jobs/WorkItemHandlers/RemoveBotEventsWorkItemHandler.cs b/src/Exceptionless.Core/Jobs/WorkItemHandlers/RemoveBotEventsWorkItemHandler.cs
index d5e879ca12..e4cfb8b7ff 100644
--- a/src/Exceptionless.Core/Jobs/WorkItemHandlers/RemoveBotEventsWorkItemHandler.cs
+++ b/src/Exceptionless.Core/Jobs/WorkItemHandlers/RemoveBotEventsWorkItemHandler.cs
@@ -17,7 +17,7 @@ public RemoveBotEventsWorkItemHandler(IEventRepository eventRepository, ILockPro
_lockProvider = lockProvider;
}
- public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = new())
+ public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = default)
{
var wi = (RemoveBotEventsWorkItem)workItem;
string cacheKey = $"{nameof(RemoveBotEventsWorkItem)}:{wi.OrganizationId}:{wi.ProjectId}";
@@ -26,7 +26,8 @@ public RemoveBotEventsWorkItemHandler(IEventRepository eventRepository, ILockPro
public override async Task HandleItemAsync(WorkItemContext context)
{
- var wi = context.GetData();
+ var wi = context.GetData()!;
+
using var _ = Log.BeginScope(new ExceptionlessState().Organization(wi.OrganizationId).Project(wi.ProjectId).Tag("Delete").Tag("Bot"));
Log.LogInformation("Received remove bot events work item OrganizationId={OrganizationId} ProjectId={ProjectId}, ClientIpAddress={ClientIpAddress}, UtcStartDate={UtcStartDate}, UtcEndDate={UtcEndDate}", wi.OrganizationId, wi.ProjectId, wi.ClientIpAddress, wi.UtcStartDate, wi.UtcEndDate);
diff --git a/src/Exceptionless.Core/Jobs/WorkItemHandlers/RemoveStacksWorkItemHandler.cs b/src/Exceptionless.Core/Jobs/WorkItemHandlers/RemoveStacksWorkItemHandler.cs
index 3b58191399..eaf9a74dd8 100644
--- a/src/Exceptionless.Core/Jobs/WorkItemHandlers/RemoveStacksWorkItemHandler.cs
+++ b/src/Exceptionless.Core/Jobs/WorkItemHandlers/RemoveStacksWorkItemHandler.cs
@@ -20,15 +20,16 @@ public RemoveStacksWorkItemHandler(IStackRepository stackRepository, ICacheClien
_lockProvider = lockProvider;
}
- public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = new())
+ public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = default)
{
string cacheKey = $"{nameof(RemoveStacksWorkItem)}:{((RemoveStacksWorkItem)workItem).ProjectId}";
- return _lockProvider.AcquireAsync(cacheKey, TimeSpan.FromMinutes(15), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(cacheKey, TimeSpan.FromMinutes(15), cancellationToken);
}
public override async Task HandleItemAsync(WorkItemContext context)
{
- var wi = context.GetData();
+ var wi = context.GetData()!;
+
using (Log.BeginScope(new ExceptionlessState().Organization(wi.OrganizationId).Project(wi.ProjectId)))
{
Log.LogInformation("Received remove stacks work item for project: {ProjectId}", wi.ProjectId);
diff --git a/src/Exceptionless.Core/Jobs/WorkItemHandlers/SetLocationFromGeoWorkItemHandler.cs b/src/Exceptionless.Core/Jobs/WorkItemHandlers/SetLocationFromGeoWorkItemHandler.cs
index 9dd197b68e..a7b0204974 100644
--- a/src/Exceptionless.Core/Jobs/WorkItemHandlers/SetLocationFromGeoWorkItemHandler.cs
+++ b/src/Exceptionless.Core/Jobs/WorkItemHandlers/SetLocationFromGeoWorkItemHandler.cs
@@ -24,20 +24,21 @@ public SetLocationFromGeoWorkItemHandler(ICacheClient cacheClient, IEventReposit
_lockProvider = lockProvider;
}
- public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = new())
+ public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = default)
{
string cacheKey = $"{nameof(SetLocationFromGeoWorkItemHandler)}:{((SetLocationFromGeoWorkItem)workItem).EventId}";
- return _lockProvider.AcquireAsync(cacheKey, TimeSpan.FromMinutes(15), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(cacheKey, TimeSpan.FromMinutes(15), cancellationToken);
}
public override async Task HandleItemAsync(WorkItemContext context)
{
- var workItem = context.GetData();
+ var workItem = context.GetData()!;
+ var geo = workItem.Geo;
- if (!GeoResult.TryParse(workItem.Geo, out var result) || result is null)
+ if (geo is null || !GeoResult.TryParse(geo, out var result) || result is null)
return;
- var location = await _cache.GetAsync(workItem.Geo, null);
+ var location = await _cache.GetAsync(geo, null);
if (location is null)
{
try
@@ -48,14 +49,14 @@ public override async Task HandleItemAsync(WorkItemContext context)
}
catch (Exception ex)
{
- Log.LogError(ex, "Error occurred looking up reverse geocode: {Geo}", workItem.Geo);
+ Log.LogError(ex, "Error occurred looking up reverse geocode: {Geo}", geo);
}
}
if (location is null)
return;
- await _cache.SetAsync(workItem.Geo, location, TimeSpan.FromDays(3));
+ await _cache.SetAsync(geo, location, TimeSpan.FromDays(3));
var ev = await _eventRepository.GetByIdAsync(workItem.EventId);
if (ev is null)
diff --git a/src/Exceptionless.Core/Jobs/WorkItemHandlers/SetProjectIsConfiguredWorkItemHandler.cs b/src/Exceptionless.Core/Jobs/WorkItemHandlers/SetProjectIsConfiguredWorkItemHandler.cs
index fa4606c15b..d4c40a8159 100644
--- a/src/Exceptionless.Core/Jobs/WorkItemHandlers/SetProjectIsConfiguredWorkItemHandler.cs
+++ b/src/Exceptionless.Core/Jobs/WorkItemHandlers/SetProjectIsConfiguredWorkItemHandler.cs
@@ -21,15 +21,16 @@ public SetProjectIsConfiguredWorkItemHandler(IProjectRepository projectRepositor
_lockProvider = lockProvider;
}
- public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = new())
+ public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = default)
{
string cacheKey = $"{nameof(SetProjectIsConfiguredWorkItemHandler)}:{((SetProjectIsConfiguredWorkItem)workItem).ProjectId}";
- return _lockProvider.AcquireAsync(cacheKey, TimeSpan.FromMinutes(15), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(cacheKey, TimeSpan.FromMinutes(15), cancellationToken);
}
public override async Task HandleItemAsync(WorkItemContext context)
{
- var workItem = context.GetData();
+ var workItem = context.GetData()!;
+
Log.LogInformation("Setting Is Configured for project: {ProjectId}", workItem.ProjectId);
var project = await _projectRepository.GetByIdAsync(workItem.ProjectId);
diff --git a/src/Exceptionless.Core/Jobs/WorkItemHandlers/UpdateProjectNotificationSettingsWorkItemHandler.cs b/src/Exceptionless.Core/Jobs/WorkItemHandlers/UpdateProjectNotificationSettingsWorkItemHandler.cs
index baf839c012..2c38fbf03b 100644
--- a/src/Exceptionless.Core/Jobs/WorkItemHandlers/UpdateProjectNotificationSettingsWorkItemHandler.cs
+++ b/src/Exceptionless.Core/Jobs/WorkItemHandlers/UpdateProjectNotificationSettingsWorkItemHandler.cs
@@ -30,14 +30,15 @@ public UpdateProjectNotificationSettingsWorkItemHandler(
_timeProvider = timeProvider;
}
- public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = new())
+ public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = default)
{
return _lockProvider.AcquireAsync(nameof(UpdateProjectNotificationSettingsWorkItemHandler), TimeSpan.FromMinutes(15), cancellationToken);
}
public override async Task HandleItemAsync(WorkItemContext context)
{
- var workItem = context.GetData();
+ var workItem = context.GetData()!;
+
Log.LogInformation("Received update project notification settings work item. Organization={Organization}", workItem.OrganizationId);
long totalNotificationSettingsRemoved = 0;
diff --git a/src/Exceptionless.Core/Jobs/WorkItemHandlers/UserMaintenanceWorkItemHandler.cs b/src/Exceptionless.Core/Jobs/WorkItemHandlers/UserMaintenanceWorkItemHandler.cs
index 0f33252f80..ae2d97f89b 100644
--- a/src/Exceptionless.Core/Jobs/WorkItemHandlers/UserMaintenanceWorkItemHandler.cs
+++ b/src/Exceptionless.Core/Jobs/WorkItemHandlers/UserMaintenanceWorkItemHandler.cs
@@ -22,16 +22,17 @@ public UserMaintenanceWorkItemHandler(IUserRepository userRepository, ILockProvi
_lockProvider = lockProvider;
}
- public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = new())
+ public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = default)
{
- return _lockProvider.AcquireAsync(nameof(UserMaintenanceWorkItemHandler), TimeSpan.FromMinutes(15), new CancellationToken(true));
+ return _lockProvider.AcquireAsync(nameof(UserMaintenanceWorkItemHandler), TimeSpan.FromMinutes(15), cancellationToken);
}
public override async Task HandleItemAsync(WorkItemContext context)
{
const int LIMIT = 100;
- var workItem = context.GetData();
+ var workItem = context.GetData()!;
+
Log.LogInformation("Received user maintenance work item. Normalize={Normalize} ResetVerifyEmailAddressToken={ResendVerifyEmailAddressEmails}", workItem.Normalize, workItem.ResetVerifyEmailAddressToken);
var results = await _userRepository.GetAllAsync(o => o.PageLimit(LIMIT));
diff --git a/src/Exceptionless.Core/Mail/Mailer.cs b/src/Exceptionless.Core/Mail/Mailer.cs
index 2c5e9403ba..b6498c1d45 100644
--- a/src/Exceptionless.Core/Mail/Mailer.cs
+++ b/src/Exceptionless.Core/Mail/Mailer.cs
@@ -303,10 +303,10 @@ private HandlebarsTemplate