diff --git a/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs b/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs index db06c90a9..5b5210c72 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs @@ -104,7 +104,7 @@ public sealed class KafkaProducer private static void LogError(ILogger log, Error error) { - log.LogWarning("Kafka error with {code} and {reason}.", error.Code, error.Reason); + LogMessages.LogKafkaError(log, error.Code, error.Reason); } public async Task SendAsync(KafkaMessageRequest job, diff --git a/backend/extensions/Squidex.Extensions/Assets/Azure/AzureMetadataSource.cs b/backend/extensions/Squidex.Extensions/Assets/Azure/AzureMetadataSource.cs index 91f089068..08dca3ff2 100644 --- a/backend/extensions/Squidex.Extensions/Assets/Azure/AzureMetadataSource.cs +++ b/backend/extensions/Squidex.Extensions/Assets/Azure/AzureMetadataSource.cs @@ -84,7 +84,7 @@ public sealed class AzureMetadataSource : IAssetMetadataSource } catch (Exception ex) { - log.LogError(ex, "Failed to enrich asset."); + LogMessages.LogFailedToEnrichAsset(log, ex); } } diff --git a/backend/extensions/Squidex.Extensions/LogMessages.cs b/backend/extensions/Squidex.Extensions/LogMessages.cs new file mode 100644 index 000000000..8d43ba40e --- /dev/null +++ b/backend/extensions/Squidex.Extensions/LogMessages.cs @@ -0,0 +1,19 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; + +namespace Squidex.Extensions; + +internal static partial class LogMessages +{ + [LoggerMessage(Level = LogLevel.Warning, Message = "Kafka error with {code} and {reason}.")] + public static partial void LogKafkaError(ILogger logger, object code, string reason); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to enrich asset.")] + public static partial void LogFailedToEnrichAsset(ILogger logger, Exception exception); +} diff --git a/backend/src/Squidex.Data.MongoDb/Infrastructure/Counts/LogMessages.cs b/backend/src/Squidex.Data.MongoDb/Infrastructure/Counts/LogMessages.cs new file mode 100644 index 000000000..e5f79581d --- /dev/null +++ b/backend/src/Squidex.Data.MongoDb/Infrastructure/Counts/LogMessages.cs @@ -0,0 +1,16 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; + +namespace Squidex.Infrastructure.Counts; + +internal static partial class LogMessages +{ + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to update count for collection {collection}.")] + public static partial void LogFailedToUpdateCount(ILogger logger, string collection, Exception exception); +} diff --git a/backend/src/Squidex.Data.MongoDb/Infrastructure/Counts/MongoCountCollection.cs b/backend/src/Squidex.Data.MongoDb/Infrastructure/Counts/MongoCountCollection.cs index ff0ab65ca..a1e397799 100644 --- a/backend/src/Squidex.Data.MongoDb/Infrastructure/Counts/MongoCountCollection.cs +++ b/backend/src/Squidex.Data.MongoDb/Infrastructure/Counts/MongoCountCollection.cs @@ -50,7 +50,7 @@ internal sealed class MongoCountCollection(IMongoDatabase database, ILogger log, } catch (Exception ex) { - log.LogError(ex, "Failed to update count for collection {collection}.", collectionName); + LogMessages.LogFailedToUpdateCount(log, collectionName, ex); } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs index f0d6ef396..b34933981 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs @@ -108,7 +108,7 @@ public sealed class RuleService( CreateJobs(@event, context, states, ct) .Catch(ex => { - log.LogError(ex, "Failed to create rule job."); + LogMessages.LogFailedToCreateRuleJob(log, ex); return states.Select(state => JobResult.Skipped(state.Rule, SkipReason.Failed)); }); @@ -237,7 +237,7 @@ public sealed class RuleService( CreateTriggerJobs(typed, triggerHandler, rulesByTrigger, context, ct) .Catch(ex => { - log.LogError(ex, "Failed to create rule jobs from trigger."); + LogMessages.LogFailedToCreateRuleJobsFromTrigger(log, ex); return states.Select(state => JobResult.Skipped(state.Rule, SkipReason.Failed)); }); @@ -263,7 +263,7 @@ public sealed class RuleService( CreateEventJobs(@event, enrichedEvent, triggerHandler, states, context) .Catch(ex => { - log.LogError(ex, "Failed to create rule jobs from event."); + LogMessages.LogFailedToCreateRuleJobsFromEvent(log, ex); return states.Select(state => new JobResult diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/LogMessages.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/LogMessages.cs new file mode 100644 index 000000000..fcd61d12e --- /dev/null +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/LogMessages.cs @@ -0,0 +1,22 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; + +namespace Squidex.Domain.Apps.Core; + +internal static partial class LogMessages +{ + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to create rule job.")] + public static partial void LogFailedToCreateRuleJob(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to create rule jobs from trigger.")] + public static partial void LogFailedToCreateRuleJobsFromTrigger(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to create rule jobs from event.")] + public static partial void LogFailedToCreateRuleJobsFromEvent(ILogger logger, Exception exception); +} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/RecursiveDeleter.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/RecursiveDeleter.cs index b4ac8dbc4..1d8962335 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/RecursiveDeleter.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/RecursiveDeleter.cs @@ -58,7 +58,7 @@ public sealed class RecursiveDeleter( } catch (Exception ex) { - log.LogError(ex, "Failed to delete asset recursively."); + LogMessages.LogFailedToDeleteAssetRecursively(log, ex); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs index fa8076159..8df210017 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs @@ -107,7 +107,7 @@ public sealed class RestoreJob( await context.LogAsync(" * Restore all objects like app, schemas and contents"); await context.LogAsync(" * Complete the restore operation for all objects"); await context.FlushAsync(); - log.LogInformation("Backup with job id {backupId} with from URL '{url}' started.", context.Job.Id, state.Url); + LogMessages.LogRestoreJobStarted(log, context.Job.Id, state.Url); state.Reader = await DownloadAsync(context, state, ct); @@ -147,7 +147,7 @@ public sealed class RestoreJob( await AssignContributorAsync(context, state); await context.LogAsync("Completed, Yeah!"); - log.LogInformation("Backup with job id {backupId} from URL '{url}' completed.", context.Job.Id, state.Url); + LogMessages.LogRestoreJobCompleted(log, context.Job.Id, state.Url); } catch (Exception ex) { @@ -168,7 +168,7 @@ public sealed class RestoreJob( await context.LogAsync(message); - log.LogError(ex, "Backup with job id {backupId} from URL '{url}' failed.", context.Job.Id, state.Url); + LogMessages.LogRestoreJobFailed(log, context.Job.Id, state.Url, ex); throw; } finally @@ -231,7 +231,7 @@ public sealed class RestoreJob( } catch (Exception ex) { - log.LogError(ex, "Failed to clean up restore."); + LogMessages.LogFailedToCleanUpRestore(log, ex); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentCollaborationHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentCollaborationHandler.cs index 33005c0c3..8701f7a69 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentCollaborationHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentCollaborationHandler.cs @@ -116,7 +116,7 @@ public sealed partial class CommentCollaborationHandler( catch (Exception ex) { // We are in an extra task, so the exception would be probably swallowed. - log.LogError(ex, "Failed to handle yjs event."); + LogMessages.LogFailedToHandleYjsEvent(log, ex); throw; } }); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Collaboration/EmailUserNotifications.cs b/backend/src/Squidex.Domain.Apps.Entities/Collaboration/EmailUserNotifications.cs index e36c232fb..e480ca3de 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Collaboration/EmailUserNotifications.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Collaboration/EmailUserNotifications.cs @@ -123,13 +123,13 @@ public sealed class EmailUserNotifications( { if (string.IsNullOrWhiteSpace(emailBody)) { - log.LogWarning("Cannot send email to {email}: No email subject configured for template {template}.", template, user.Email); + LogMessages.LogNoEmailSubjectConfigured(log, template, user.Email); return; } if (string.IsNullOrWhiteSpace(emailSubj)) { - log.LogWarning("Cannot send email to {email}: No email body configured for template {template}.", template, user.Email); + LogMessages.LogNoEmailBodyConfigured(log, template, user.Email); return; } @@ -146,7 +146,7 @@ public sealed class EmailUserNotifications( } catch (Exception ex) { - log.LogError(ex, "Failed to send notification to {email}.", user.Email); + LogMessages.LogFailedToSendNotification(log, user.Email, ex); throw; } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerProcess.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerProcess.cs index 56090ef55..6910b6e11 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerProcess.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerProcess.cs @@ -55,7 +55,7 @@ public sealed class ContentSchedulerProcess( } catch (Exception ex) { - log.LogError(ex, "Failed to query scheduled status changes-"); + LogMessages.LogFailedToQueryScheduledStatusChanges(log, ex); } } @@ -88,7 +88,7 @@ public sealed class ContentSchedulerProcess( } catch (Exception ex) { - log.LogError(ex, "Failed to execute scheduled status change for content '{contentId}'.", content.Id); + LogMessages.LogFailedToExecuteScheduledStatusChange(log, content.Id, ex); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ErrorVisitor.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ErrorVisitor.cs index f5c5c3f37..7bb372f0e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ErrorVisitor.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ErrorVisitor.cs @@ -26,11 +26,11 @@ internal static class ErrorVisitor if (!string.IsNullOrWhiteSpace(fieldName)) { - log.LogError(context.OriginalException, "Failed to resolve field {field}.", fieldName); + LogMessages.LogFailedToResolveField(log, fieldName, context.OriginalException); } else { - log.LogError(context.OriginalException, "Failed to resolve execute query."); + LogMessages.LogFailedToResolveQuery(log, context.OriginalException); } if (context.OriginalException is ValidationException or DomainException) diff --git a/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs b/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs index 4a9943f32..e78149eb0 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs @@ -128,11 +128,11 @@ public class NotifoService : IUserEvents } catch (NotifoException ex) { - log.LogError(ex, "Failed to register user in notifo: {details}.", ex.ToString()); + LogMessages.LogFailedToRegisterUserInNotifoWithDetails(log, ex.ToString(), ex); } catch (Exception ex) { - log.LogError(ex, "Failed to register user in notifo."); + LogMessages.LogFailedToRegisterUserInNotifo(log, ex); } } @@ -191,11 +191,11 @@ public class NotifoService : IUserEvents } catch (NotifoException ex) { - log.LogError(ex, "Failed to push user to notifo: {details}.", ex.ToString()); + LogMessages.LogFailedToPushUserToNotifoWithDetails(log, ex.ToString(), ex); } catch (Exception ex) { - log.LogError(ex, "Failed to push user to notifo."); + LogMessages.LogFailedToPushUserToNotifo(log, ex); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Invitation/InvitationEventConsumer.cs b/backend/src/Squidex.Domain.Apps.Entities/Invitation/InvitationEventConsumer.cs index 45854d0bb..853c15372 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Invitation/InvitationEventConsumer.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Invitation/InvitationEventConsumer.cs @@ -106,7 +106,7 @@ public sealed class InvitationEventConsumer( if (assigner == null) { - log.LogWarning("Failed to invite user: Assigner {assignerId} not found.", assignerId); + LogMessages.LogInvitationAssignerNotFound(log, assignerId); return default; } @@ -114,7 +114,7 @@ public sealed class InvitationEventConsumer( if (assignee == null) { - log.LogWarning("Failed to invite user: Assignee {assigneeId} not found.", assigneeId); + LogMessages.LogInvitationAssigneeNotFound(log, assigneeId); return default; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs b/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs index 601371d3b..073a13e68 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs @@ -59,7 +59,7 @@ public sealed class JobProcessor if (pending.Any()) { // This should actually never happen, so we log with warning. - log.LogWarning("Removed unfinished jobs for owner {ownerId} after start.", ownerId); + LogMessages.LogRemovedUnfinishedJobs(log, ownerId); foreach (var job in pending.ToList()) { @@ -81,7 +81,7 @@ public sealed class JobProcessor { return scheduler.ScheduleAsync(async _ => { - log.LogInformation("Clearing jobs for owner {ownerId}.", ownerId); + LogMessages.LogClearingJobs(log, ownerId); var job = state.Value.Jobs.Find(x => x.Id == jobId); @@ -105,7 +105,7 @@ public sealed class JobProcessor { return scheduler.ScheduleAsync(async _ => { - log.LogInformation("Clearing jobs for owner {ownerId}.", ownerId); + LogMessages.LogClearingJobs(log, ownerId); foreach (var job in state.Value.Jobs) { @@ -164,7 +164,7 @@ public sealed class JobProcessor OwnerId = ownerId, }; - log.LogInformation("Starting new backup with backup id '{backupId}' for owner {ownerId}.", context.Job.Id, ownerId); + LogMessages.LogStartingJob(log, context.Job.Id, ownerId); state.Value.Jobs.Insert(0, context.Job); try @@ -221,7 +221,7 @@ public sealed class JobProcessor } catch (Exception ex) { - log.LogError(ex, "Failed to run job with ID {jobId}.", context.Job.Id); + LogMessages.LogFailedToRunJob(log, context.Job.Id, ex); await SetStatusAsync(context, JobStatus.Failed); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/LogMessages.cs b/backend/src/Squidex.Domain.Apps.Entities/LogMessages.cs new file mode 100644 index 000000000..44d7ed2a5 --- /dev/null +++ b/backend/src/Squidex.Domain.Apps.Entities/LogMessages.cs @@ -0,0 +1,92 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Entities; + +internal static partial class LogMessages +{ + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to register user in notifo: {details}.")] + public static partial void LogFailedToRegisterUserInNotifoWithDetails(ILogger logger, string details, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to register user in notifo.")] + public static partial void LogFailedToRegisterUserInNotifo(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to push user to notifo: {details}.")] + public static partial void LogFailedToPushUserToNotifoWithDetails(ILogger logger, string details, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to push user to notifo.")] + public static partial void LogFailedToPushUserToNotifo(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to delete asset recursively.")] + public static partial void LogFailedToDeleteAssetRecursively(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Information, Message = "Backup with job id {backupId} with from URL '{url}' started.")] + public static partial void LogRestoreJobStarted(ILogger logger, DomainId backupId, Uri url); + + [LoggerMessage(Level = LogLevel.Information, Message = "Backup with job id {backupId} from URL '{url}' completed.")] + public static partial void LogRestoreJobCompleted(ILogger logger, DomainId backupId, Uri url); + + [LoggerMessage(Level = LogLevel.Error, Message = "Backup with job id {backupId} from URL '{url}' failed.")] + public static partial void LogRestoreJobFailed(ILogger logger, DomainId backupId, Uri url, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to clean up restore.")] + public static partial void LogFailedToCleanUpRestore(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to handle yjs event.")] + public static partial void LogFailedToHandleYjsEvent(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Warning, Message = "Cannot send email to {email}: No email subject configured for template {template}.")] + public static partial void LogNoEmailSubjectConfigured(ILogger logger, string email, string template); + + [LoggerMessage(Level = LogLevel.Warning, Message = "Cannot send email to {email}: No email body configured for template {template}.")] + public static partial void LogNoEmailBodyConfigured(ILogger logger, string email, string template); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to send notification to {email}.")] + public static partial void LogFailedToSendNotification(ILogger logger, string email, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to query scheduled status changes-")] + public static partial void LogFailedToQueryScheduledStatusChanges(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to execute scheduled status change for content '{contentId}'.")] + public static partial void LogFailedToExecuteScheduledStatusChange(ILogger logger, DomainId contentId, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to resolve field {field}.")] + public static partial void LogFailedToResolveField(ILogger logger, string field, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to resolve execute query.")] + public static partial void LogFailedToResolveQuery(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Warning, Message = "Failed to invite user: Assigner {assignerId} not found.")] + public static partial void LogInvitationAssignerNotFound(ILogger logger, RefToken assignerId); + + [LoggerMessage(Level = LogLevel.Warning, Message = "Failed to invite user: Assignee {assigneeId} not found.")] + public static partial void LogInvitationAssigneeNotFound(ILogger logger, string assigneeId); + + [LoggerMessage(Level = LogLevel.Warning, Message = "Removed unfinished jobs for owner {ownerId} after start.")] + public static partial void LogRemovedUnfinishedJobs(ILogger logger, DomainId ownerId); + + [LoggerMessage(Level = LogLevel.Information, Message = "Clearing jobs for owner {ownerId}.")] + public static partial void LogClearingJobs(ILogger logger, DomainId ownerId); + + [LoggerMessage(Level = LogLevel.Information, Message = "Starting new backup with backup id '{backupId}' for owner {ownerId}.")] + public static partial void LogStartingJob(ILogger logger, DomainId backupId, DomainId ownerId); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to run job with ID {jobId}.")] + public static partial void LogFailedToRunJob(ILogger logger, DomainId jobId, Exception exception); + + [LoggerMessage(Level = LogLevel.Information, Message = "Adding rule job for Rule(trigger={ruleTrigger})")] + public static partial void LogAddingRuleJob(ILogger logger, string ruleTrigger); + + [LoggerMessage(Level = LogLevel.Warning, Message = "Failed to run rule with ID {ruleId}, continue with next job.")] + public static partial void LogFailedToRunRule(ILogger logger, DomainId? ruleId, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to execute search from source {source} with query '{query}'.")] + public static partial void LogFailedToExecuteSearch(ILogger logger, string source, string query, Exception exception); +} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleQueueWriter.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleQueueWriter.cs index a7b0b8e79..4677de644 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleQueueWriter.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleQueueWriter.cs @@ -35,8 +35,10 @@ public sealed class RuleQueueWriter(IFlowManager flowManager, writes.Add(result.Job.Value); - log?.LogInformation("Adding rule job for Rule(trigger={ruleTrigger})", - result.Rule.Trigger.GetType().Name); + if (log != null) + { + LogMessages.LogAddingRuleJob(log, result.Rule.Trigger.GetType().Name); + } var today = Clock.GetCurrentInstant().ToDateOnly(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerJob.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerJob.cs index a38943b71..4628dba7e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerJob.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerJob.cs @@ -171,8 +171,7 @@ public sealed class RuleRunnerJob : IJobRunner throw result.EnrichmentError; } - log.LogWarning(result.EnrichmentError, "Failed to run rule with ID {ruleId}, continue with next job.", - result.Rule?.Id); + LogMessages.LogFailedToRunRule(log, result.Rule?.Id, result.EnrichmentError); } } } @@ -212,8 +211,7 @@ public sealed class RuleRunnerJob : IJobRunner throw result.EnrichmentError; } - log.LogWarning(result.EnrichmentError, "Failed to run rule with ID {ruleId}, continue with next job.", - result.Rule?.Id); + LogMessages.LogFailedToRunRule(log, result.Rule?.Id, result.EnrichmentError); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Search/SearchManager.cs b/backend/src/Squidex.Domain.Apps.Entities/Search/SearchManager.cs index fcde9e6f1..9c6e03f27 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Search/SearchManager.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Search/SearchManager.cs @@ -37,7 +37,7 @@ public sealed class SearchManager(IEnumerable searchSources, ILog } catch (Exception ex) { - log.LogError(ex, "Failed to execute search from source {source} with query '{query}'.", source, query); + LogMessages.LogFailedToExecuteSearch(log, source.GetType().Name, query, ex); return Empty; } } diff --git a/backend/src/Squidex.Domain.Users/DefaultUserService.cs b/backend/src/Squidex.Domain.Users/DefaultUserService.cs index 4717c1bcb..6f97a7798 100644 --- a/backend/src/Squidex.Domain.Users/DefaultUserService.cs +++ b/backend/src/Squidex.Domain.Users/DefaultUserService.cs @@ -195,7 +195,7 @@ public sealed class DefaultUserService( } catch (Exception ex2) { - log.LogError(ex2, "Failed to cleanup user after creation failed."); + LogMessages.LogFailedToCleanupUser(log, ex2); } throw; diff --git a/backend/src/Squidex.Domain.Users/LogMessages.cs b/backend/src/Squidex.Domain.Users/LogMessages.cs new file mode 100644 index 000000000..24d9682ae --- /dev/null +++ b/backend/src/Squidex.Domain.Users/LogMessages.cs @@ -0,0 +1,19 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; + +namespace Squidex.Domain.Users; + +internal static partial class LogMessages +{ + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to cleanup user after creation failed.")] + public static partial void LogFailedToCleanupUser(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Identity operation failed: {errorMessage}.")] + public static partial void LogIdentityOperationFailed(ILogger logger, string errorMessage); +} diff --git a/backend/src/Squidex.Domain.Users/UserManagerExtensions.cs b/backend/src/Squidex.Domain.Users/UserManagerExtensions.cs index daceb64a8..4826b7f6c 100644 --- a/backend/src/Squidex.Domain.Users/UserManagerExtensions.cs +++ b/backend/src/Squidex.Domain.Users/UserManagerExtensions.cs @@ -46,7 +46,7 @@ internal static class UserManagerExtensions var errorMessage = errorMessageBuilder.ToString(); - log.LogError("Identity operation failed: {errorMessage}.", errorMessage); + LogMessages.LogIdentityOperationFailed(log, errorMessage); throw new ValidationException(result.Errors.Select(x => new ValidationError(Localize(x))).ToList()); } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Users/UsersController.cs b/backend/src/Squidex/Areas/Api/Controllers/Users/UsersController.cs index 4ca1d1dbf..8be988c82 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Users/UsersController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Users/UsersController.cs @@ -98,7 +98,7 @@ public sealed class UsersController( } catch (Exception ex) { - log.LogError(ex, "Failed to return users, returning empty results."); + LogMessages.LogFailedToReturnUsers(log, ex); } return Ok(Array.Empty()); @@ -129,7 +129,7 @@ public sealed class UsersController( } catch (Exception ex) { - log.LogError(ex, "Failed to return user, returning empty results."); + LogMessages.LogFailedToReturnUser(log, ex); } return NotFound(); @@ -202,7 +202,7 @@ public sealed class UsersController( } catch (Exception ex) { - log.LogError(ex, "Failed to return user picture, returning fallback image."); + LogMessages.LogFailedToReturnUserPicture(log, ex); } return new FileStreamResult(new MemoryStream(AvatarBytes), "image/png"); diff --git a/backend/src/Squidex/Areas/IdentityServer/Config/CreateAdminInitializer.cs b/backend/src/Squidex/Areas/IdentityServer/Config/CreateAdminInitializer.cs index 4cdea2c1d..bc86685d4 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Config/CreateAdminInitializer.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Config/CreateAdminInitializer.cs @@ -84,7 +84,7 @@ public sealed class CreateAdminInitializer(IServiceProvider serviceProvider) : I { var log = serviceProvider.GetRequiredService>(); - log.LogError(ex, "Failed to create administrator."); + LogMessages.LogFailedToCreateAdministrator(log, ex); } } diff --git a/backend/src/Squidex/LogMessages.cs b/backend/src/Squidex/LogMessages.cs new file mode 100644 index 000000000..973d41ecb --- /dev/null +++ b/backend/src/Squidex/LogMessages.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; + +namespace Squidex; + +internal static partial class LogMessages +{ + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to create administrator.")] + public static partial void LogFailedToCreateAdministrator(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to return users, returning empty results.")] + public static partial void LogFailedToReturnUsers(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to return user, returning empty results.")] + public static partial void LogFailedToReturnUser(ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Error, Message = "Failed to return user picture, returning fallback image.")] + public static partial void LogFailedToReturnUserPicture(ILogger logger, Exception exception); +}