diff --git a/src/Turnierplan.App/Endpoints/Documents/CreateDocumentEndpoint.cs b/src/Turnierplan.App/Endpoints/Documents/CreateDocumentEndpoint.cs index a05a4b53..e2faad29 100644 --- a/src/Turnierplan.App/Endpoints/Documents/CreateDocumentEndpoint.cs +++ b/src/Turnierplan.App/Endpoints/Documents/CreateDocumentEndpoint.cs @@ -11,7 +11,7 @@ namespace Turnierplan.App.Endpoints.Documents; -internal sealed class CreateDocumentEndpoint : EndpointBase +internal sealed partial class CreateDocumentEndpoint : EndpointBase { protected override HttpMethod Method => HttpMethod.Post; @@ -53,7 +53,7 @@ private static async Task Handle( if (!documentTypeRegistry.TryGetDocumentDefaultConfiguration(request.Type, out var configuration)) { - logger.LogCritical("Could not get the default document configuration for document type {DocumentType}.", request.Type); + CouldNotGetDefaultDocumentConfiguration(logger, request.Type); return Results.InternalServerError(); } @@ -85,4 +85,7 @@ private Validator() .NotEmpty(); } } + + [LoggerMessage(LogLevel.Critical, "Could not get the default document configuration for document type {DocumentType}.", EventId = 101)] + private static partial void CouldNotGetDefaultDocumentConfiguration(ILogger logger, DocumentType documentType); } diff --git a/src/Turnierplan.App/Endpoints/Documents/GetDocumentPdfEndpoint.cs b/src/Turnierplan.App/Endpoints/Documents/GetDocumentPdfEndpoint.cs index 7c3187fb..a8a9dfe5 100644 --- a/src/Turnierplan.App/Endpoints/Documents/GetDocumentPdfEndpoint.cs +++ b/src/Turnierplan.App/Endpoints/Documents/GetDocumentPdfEndpoint.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Turnierplan.App.OpenApi; using Turnierplan.App.Security; +using Turnierplan.Core.Document; using Turnierplan.Core.PublicId; using Turnierplan.Dal.Extensions; using Turnierplan.Dal.Repositories; @@ -10,7 +11,7 @@ namespace Turnierplan.App.Endpoints.Documents; -internal sealed class GetDocumentPdfEndpoint : EndpointBase +internal sealed partial class GetDocumentPdfEndpoint : EndpointBase { protected override HttpMethod Method => HttpMethod.Get; @@ -59,7 +60,7 @@ private static async Task Handle( if (!documentTypeRegistry.TryParseDocumentConfiguration(document.Type, document.Configuration, out var configuration)) { - logger.LogError("Failed to parse the document configuration of document with type '{DocumentType}'.", document.Type); + FailedToParseDocumentConfiguration(logger, document.Type); return Results.InternalServerError(); } @@ -68,7 +69,7 @@ private static async Task Handle( if (renderer is null) { - logger.LogCritical("No document renderer available for document type '{DocumentType}'.", document.Type); + NoRendererAvailableForDocumentType(logger, document.Type); return Results.InternalServerError(); } @@ -95,4 +96,10 @@ private static async Task Handle( return Results.File(stream.ToArray(), "application/pdf"); } + + [LoggerMessage(LogLevel.Error, "Failed to parse the document configuration of document with type '{DocumentType}'.", EventId = 101)] + private static partial void FailedToParseDocumentConfiguration(ILogger logger, DocumentType documentType); + + [LoggerMessage(LogLevel.Critical, "No document renderer available for document type '{DocumentType}'.", EventId = 102)] + private static partial void NoRendererAvailableForDocumentType(ILogger logger, DocumentType documentType); } diff --git a/src/Turnierplan.App/Helpers/ApplicationUrlProvider.cs b/src/Turnierplan.App/Helpers/ApplicationUrlProvider.cs index dbe856f3..df5ab782 100644 --- a/src/Turnierplan.App/Helpers/ApplicationUrlProvider.cs +++ b/src/Turnierplan.App/Helpers/ApplicationUrlProvider.cs @@ -7,12 +7,12 @@ namespace Turnierplan.App.Helpers; internal sealed class ApplicationUrlProvider : IApplicationUrlProvider { private readonly IOptionsMonitor _options; - private readonly ILogger _logger; + private readonly ApplicationUrlProviderLogger _logger; public ApplicationUrlProvider(IOptionsMonitor options, ILogger logger) { _options = options; - _logger = logger; + _logger = new ApplicationUrlProviderLogger(logger); } public string GetApplicationUrl() @@ -21,7 +21,7 @@ public string GetApplicationUrl() if (string.IsNullOrWhiteSpace(url)) { - _logger.LogWarning("The ApplicationUrl is not specified. Please check your application configuration."); + _logger.ApplicationUrlNotSpecified(); return string.Empty; } diff --git a/src/Turnierplan.App/Helpers/ApplicationUrlProviderLogger.cs b/src/Turnierplan.App/Helpers/ApplicationUrlProviderLogger.cs new file mode 100644 index 00000000..60121e08 --- /dev/null +++ b/src/Turnierplan.App/Helpers/ApplicationUrlProviderLogger.cs @@ -0,0 +1,14 @@ +namespace Turnierplan.App.Helpers; + +internal sealed partial class ApplicationUrlProviderLogger +{ + private readonly ILogger _logger; + + public ApplicationUrlProviderLogger(ILogger logger) + { + _logger = logger; + } + + [LoggerMessage(LogLevel.Warning, "The 'ApplicationUrl' is not specified. Please check your application configuration.", EventId = 101)] + public partial void ApplicationUrlNotSpecified(); +} diff --git a/src/Turnierplan.App/Helpers/DeletionHelper.cs b/src/Turnierplan.App/Helpers/DeletionHelper.cs index e4b51301..3fff5884 100644 --- a/src/Turnierplan.App/Helpers/DeletionHelper.cs +++ b/src/Turnierplan.App/Helpers/DeletionHelper.cs @@ -17,7 +17,7 @@ internal sealed class DeletionHelper : IDeletionHelper private readonly IPlanningRealmRepository _planningRealmRepository; private readonly IImageRepository _imageRepository; private readonly IImageStorage _imageStorage; - private readonly ILogger _logger; + private readonly DeletionHelperLogger _logger; public DeletionHelper( IOrganizationRepository organizationRepository, @@ -34,7 +34,7 @@ public DeletionHelper( _planningRealmRepository = planningRealmRepository; _imageRepository = imageRepository; _imageStorage = imageStorage; - _logger = logger; + _logger = new DeletionHelperLogger(logger); } public async Task DeleteOrganizationAsync(Organization organization, CancellationToken cancellationToken) @@ -57,14 +57,14 @@ public async Task DeleteOrganizationAsync(Organization organization, Cance } catch (Exception ex) { - _logger.LogError(ex, "Image with id '{ImageId}' was successfully deleted from image storage but the deletion from the database failed.", image.Id); + _logger.ImageDeletedButDatabaseRemovalFailed(ex, image.Id); return false; } } else { - _logger.LogError("Failed to delete image with id '{ImageId}' from image storage while deleting organization with id '{OrganizationId}'.", image.Id, organization.Id); + _logger.ImageDeletionFromStorageFailed(image.Id, organization.Id); hasNonDeletedImages = true; } diff --git a/src/Turnierplan.App/Helpers/DeletionHelperLogger.cs b/src/Turnierplan.App/Helpers/DeletionHelperLogger.cs new file mode 100644 index 00000000..18d70979 --- /dev/null +++ b/src/Turnierplan.App/Helpers/DeletionHelperLogger.cs @@ -0,0 +1,17 @@ +namespace Turnierplan.App.Helpers; + +internal sealed partial class DeletionHelperLogger +{ + private readonly ILogger _logger; + + public DeletionHelperLogger(ILogger logger) + { + _logger = logger; + } + + [LoggerMessage(LogLevel.Error, "Image with id '{ImageId}' was successfully deleted from image storage but the deletion from the database failed.", EventId = 101)] + public partial void ImageDeletedButDatabaseRemovalFailed(Exception exception, long imageId); + + [LoggerMessage(LogLevel.Error, "Failed to delete image with id '{ImageId}' from image storage while deleting organization with id '{OrganizationId}'.", EventId = 102)] + public partial void ImageDeletionFromStorageFailed(long imageId, long organizationId); +} diff --git a/src/Turnierplan.App/Security/SigningKeyProvider.cs b/src/Turnierplan.App/Security/SigningKeyProvider.cs index a5d3936c..11ac5187 100644 --- a/src/Turnierplan.App/Security/SigningKeyProvider.cs +++ b/src/Turnierplan.App/Security/SigningKeyProvider.cs @@ -26,7 +26,8 @@ public SigningKeyProvider(IOptions options, ILogger _logger; + + public SigningKeyProviderLogger(ILogger logger) + { + _logger = logger; + } + + [LoggerMessage(LogLevel.Critical, "The directory for identity storage does not exist and could not be created.", EventId = 101)] + public partial void IdentityDirectoryCreationFailed(); +} diff --git a/src/Turnierplan.Dal/TurnierplanContext.cs b/src/Turnierplan.Dal/TurnierplanContext.cs index 8c8a25eb..9f55fc1e 100644 --- a/src/Turnierplan.Dal/TurnierplanContext.cs +++ b/src/Turnierplan.Dal/TurnierplanContext.cs @@ -20,14 +20,14 @@ public sealed class TurnierplanContext : DbContext, IUnitOfWork { public const string Schema = "turnierplan"; - private readonly ILogger _logger; + private readonly TurnierplanContextLogger _logger; private IDbContextTransaction? _activeTransaction; public TurnierplanContext(DbContextOptions options, ILogger logger) : base(options) { - _logger = logger; + _logger = new TurnierplanContextLogger(logger); } public DbSet ApiKeys { get; set; } = null!; @@ -95,7 +95,7 @@ public async Task BeginTransactionAsync() throw new InvalidOperationException("Transaction already started."); } - _logger.LogInformation("Beginning database transaction"); + _logger.BeginningTransaction(); _activeTransaction = await Database.BeginTransactionAsync(); } @@ -107,7 +107,7 @@ public async Task CommitTransactionAsync() throw new InvalidOperationException("Transaction not started."); } - _logger.LogInformation("Committing database transaction"); + _logger.CommittingTransaction(); await _activeTransaction.CommitAsync(); await _activeTransaction.DisposeAsync(); @@ -122,7 +122,7 @@ public async Task RollbackTransactionAsync() throw new InvalidOperationException("Transaction not started."); } - _logger.LogInformation("Rolling back database transaction"); + _logger.RollingBackTransaction(); await _activeTransaction.RollbackAsync(); await _activeTransaction.DisposeAsync(); diff --git a/src/Turnierplan.Dal/TurnierplanContextLogger.cs b/src/Turnierplan.Dal/TurnierplanContextLogger.cs new file mode 100644 index 00000000..03b437b7 --- /dev/null +++ b/src/Turnierplan.Dal/TurnierplanContextLogger.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Logging; + +namespace Turnierplan.Dal; + +internal sealed partial class TurnierplanContextLogger +{ + private readonly ILogger _logger; + + public TurnierplanContextLogger(ILogger logger) + { + _logger = logger; + } + + [LoggerMessage(LogLevel.Information, "Beginning database transaction", EventId = 101)] + public partial void BeginningTransaction(); + + [LoggerMessage(LogLevel.Information, "Committing database transaction", EventId = 102)] + public partial void CommittingTransaction(); + + [LoggerMessage(LogLevel.Information, "Rolling back database transaction", EventId = 103)] + public partial void RollingBackTransaction(); +} diff --git a/src/Turnierplan.ImageStorage/Azure/AzureImageStorage.cs b/src/Turnierplan.ImageStorage/Azure/AzureImageStorage.cs index 0ab2f9e5..f524664b 100644 --- a/src/Turnierplan.ImageStorage/Azure/AzureImageStorage.cs +++ b/src/Turnierplan.ImageStorage/Azure/AzureImageStorage.cs @@ -10,14 +10,14 @@ namespace Turnierplan.ImageStorage.Azure; internal sealed class AzureImageStorage : IImageStorage { - private readonly ILogger _logger; + private readonly AzureImageStorageLogger _logger; private readonly BlobContainerClient _client; private readonly string _storageAccountUrl; private readonly string _containerName; public AzureImageStorage(IOptions options, ILogger logger) { - _logger = logger; + _logger = new AzureImageStorageLogger(logger); ArgumentException.ThrowIfNullOrWhiteSpace(options.Value.StorageAccountName); ArgumentException.ThrowIfNullOrWhiteSpace(options.Value.ContainerName); @@ -25,7 +25,7 @@ public AzureImageStorage(IOptions options, ILogger options, ILogger options, ILogger SaveImageAsync(Image image, MemoryStream imageData) } catch (Exception ex) { - _logger.LogError(ex, "Failed to upload image '{BlobName}' to Azure Blob Storage because of an exception.", blobName); + _logger.FailedToUploadImage(ex, blobName); } return false; @@ -110,7 +110,7 @@ public async Task GetImageAsync(Image image) } catch (Exception ex) { - _logger.LogError(ex, "Failed to read image '{BlobName}' from Azure Blob Storage because of an exception.", blobName); + _logger.FailedToReadImage(ex, blobName); throw new InvalidOperationException("Failed to read image from Azure Blob Storage.", ex); } @@ -129,7 +129,7 @@ public async Task DeleteImageAsync(Image image) } catch (Exception ex) { - _logger.LogError(ex, "Failed to delete image '{BlobName}' from Azure Blob Storage because of an exception.", blobName); + _logger.FailedToDeleteImage(ex, blobName); } return false; diff --git a/src/Turnierplan.ImageStorage/Azure/AzureImageStorageLogger.cs b/src/Turnierplan.ImageStorage/Azure/AzureImageStorageLogger.cs new file mode 100644 index 00000000..de0f7bf5 --- /dev/null +++ b/src/Turnierplan.ImageStorage/Azure/AzureImageStorageLogger.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.Logging; + +namespace Turnierplan.ImageStorage.Azure; + +internal sealed partial class AzureImageStorageLogger +{ + private readonly ILogger _logger; + + public AzureImageStorageLogger(ILogger logger) + { + _logger = logger; + } + + [LoggerMessage(LogLevel.Information, "Initializing Azure Blob Storage client for storage account '{StorageAccountUrl}'.", EventId = 101)] + public partial void InitializingAzureBlobStorageClient(string storageAccountUrl); + + [LoggerMessage(LogLevel.Information, "Using account key authentication for Azure Blob Storage", EventId = 102)] + public partial void UsingAccountKeyAuthentication(); + + [LoggerMessage(LogLevel.Information, "Using ClientSecretCredential for Azure Blob Storage", EventId = 103)] + public partial void UsingClientSecretCredential(); + + [LoggerMessage(LogLevel.Information, "Using DefaultAzureCredential for Azure Blob Storage", EventId = 104)] + public partial void UsingDefaultAzureCredential(); + + [LoggerMessage(LogLevel.Error, "Failed to upload image '{BlobName}' to Azure Blob Storage because of an exception.", EventId = 105)] + public partial void FailedToUploadImage(Exception exception, string blobName); + + [LoggerMessage(LogLevel.Error, "Failed to read image '{BlobName}' from Azure Blob Storage because of an exception.", EventId = 106)] + public partial void FailedToReadImage(Exception exception, string blobName); + + [LoggerMessage(LogLevel.Error, "Failed to delete image '{BlobName}' from Azure Blob Storage because of an exception.", EventId = 107)] + public partial void FailedToDeleteImage(Exception exception, string blobName); +} diff --git a/src/Turnierplan.ImageStorage/Local/LocalImageStorage.cs b/src/Turnierplan.ImageStorage/Local/LocalImageStorage.cs index 85be3a5a..ee449df6 100644 --- a/src/Turnierplan.ImageStorage/Local/LocalImageStorage.cs +++ b/src/Turnierplan.ImageStorage/Local/LocalImageStorage.cs @@ -9,23 +9,23 @@ namespace Turnierplan.ImageStorage.Local; internal sealed class LocalImageStorage : ILocalImageStorage { - private readonly ILogger _logger; + private readonly LocalImageStorageLogger _logger; private readonly string _storagePath; public LocalImageStorage(ILogger logger, IOptions options) { - _logger = logger; + _logger = new LocalImageStorageLogger(logger); ArgumentException.ThrowIfNullOrWhiteSpace(options.Value.StoragePath); _storagePath = Path.GetFullPath(options.Value.StoragePath); - _logger.LogInformation("Using the following directory for local image storage: '{LocalImageStoragePath}'", _storagePath); + _logger.UsingDirectoryForLocalImageStorage(_storagePath); Directory.CreateDirectory(_storagePath); if (!Directory.Exists(_storagePath)) { - _logger.LogCritical("The directory for local image storage does not exist and could not be created."); + _logger.DirectoryCouldNotBeCreated(); } } @@ -50,13 +50,13 @@ public Task SaveImageAsync(Image image, MemoryStream imageData) if (!Directory.Exists(imageDirectoryPath)) { - _logger.LogCritical("The directory for the image could not be created: '{ImageDirectory}'.", imageDirectoryPath); + _logger.DirectoryForImageCouldNotBeCreated(imageDirectoryPath); return Task.FromResult(false); } } - _logger.LogDebug("Writing image to: {FilePath}", filePath); + _logger.WritingImageToFile(filePath); using var destination = new FileStream(filePath, FileMode.Create); imageData.CopyTo(destination); @@ -65,7 +65,7 @@ public Task SaveImageAsync(Image image, MemoryStream imageData) } catch (Exception ex) { - _logger.LogError(ex, "Failed to save image to file to '{FilePath}'.", filePath); + _logger.FailedToWriteImage(ex, filePath); return Task.FromResult(false); } @@ -86,7 +86,7 @@ public Task DeleteImageAsync(Image image) } catch (Exception ex) { - _logger.LogError(ex, "Failed to delete image file '{FilePath}'.", filePath); + _logger.FailedToDeleteImage(ex, filePath); return Task.FromResult(false); } diff --git a/src/Turnierplan.ImageStorage/Local/LocalImageStorageLogger.cs b/src/Turnierplan.ImageStorage/Local/LocalImageStorageLogger.cs new file mode 100644 index 00000000..8a93ae51 --- /dev/null +++ b/src/Turnierplan.ImageStorage/Local/LocalImageStorageLogger.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Logging; + +namespace Turnierplan.ImageStorage.Local; + +internal sealed partial class LocalImageStorageLogger +{ + private readonly ILogger _logger; + + public LocalImageStorageLogger(ILogger logger) + { + _logger = logger; + } + + [LoggerMessage(LogLevel.Information, "Using the following directory for local image storage: '{LocalImageStoragePath}'", EventId = 101)] + public partial void UsingDirectoryForLocalImageStorage(string localImageStoragePath); + + [LoggerMessage(LogLevel.Critical, "The directory for local image storage does not exist and could not be created.", EventId = 102)] + public partial void DirectoryCouldNotBeCreated(); + + [LoggerMessage(LogLevel.Critical, "The directory for the image could not be created: '{ImageDirectory}'.", EventId = 103)] + public partial void DirectoryForImageCouldNotBeCreated(string imageDirectory); + + [LoggerMessage(LogLevel.Debug, "Writing image to: {FilePath}", EventId = 104)] + public partial void WritingImageToFile(string filePath); + + [LoggerMessage(LogLevel.Error, "Failed to save image to file to '{FilePath}'.", EventId = 105)] + public partial void FailedToWriteImage(Exception exception, string filePath); + + [LoggerMessage(LogLevel.Error, "Failed to delete image file '{FilePath}'.", EventId = 106)] + public partial void FailedToDeleteImage(Exception exception, string filePath); +} diff --git a/src/Turnierplan.ImageStorage/S3/S3ImageStorage.cs b/src/Turnierplan.ImageStorage/S3/S3ImageStorage.cs index f1a3705a..f12cb2cc 100644 --- a/src/Turnierplan.ImageStorage/S3/S3ImageStorage.cs +++ b/src/Turnierplan.ImageStorage/S3/S3ImageStorage.cs @@ -11,7 +11,7 @@ namespace Turnierplan.ImageStorage.S3; internal sealed class S3ImageStorage : IImageStorage { - private readonly ILogger _logger; + private readonly S3ImageStorageLogger _logger; private readonly AmazonS3Client _client; private readonly string _bucketName; @@ -21,7 +21,7 @@ public S3ImageStorage(IOptions options, ILogger SaveImageAsync(Image image, MemoryStream imageData) return true; } - _logger.LogError("Failed to upload image '{ObjectKey}' to S3. Result status code: {StatusCode}", objectKey, (int)response.HttpStatusCode); + _logger.FailedToUploadImage(objectKey, (int)response.HttpStatusCode); } catch (Exception ex) { - _logger.LogError(ex, "Failed to upload image '{ObjectKey}' to S3 because of an exception.", objectKey); + _logger.FailedToUploadImage(ex, objectKey); } return false; @@ -112,7 +112,7 @@ public async Task GetImageAsync(Image image) return response.ResponseStream; } - _logger.LogError("Failed to read image '{ObjectKey}' from S3. Result status code: {StatusCode}", objectKey, (int)response.HttpStatusCode); + _logger.FailedToReadImage(objectKey, (int)response.HttpStatusCode); throw new InvalidOperationException($"Failed to read image from S3. Status code: {response.HttpStatusCode}"); } @@ -138,7 +138,7 @@ public async Task DeleteImageAsync(Image image) } catch (Exception ex) { - _logger.LogError(ex, "Failed to delete image '{ObjectKey}' from S3 because of an exception.", objectKey); + _logger.FailedToDeleteImage(ex, objectKey); } return false; diff --git a/src/Turnierplan.ImageStorage/S3/S3ImageStorageLogger.cs b/src/Turnierplan.ImageStorage/S3/S3ImageStorageLogger.cs new file mode 100644 index 00000000..585a7cf4 --- /dev/null +++ b/src/Turnierplan.ImageStorage/S3/S3ImageStorageLogger.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.Logging; + +namespace Turnierplan.ImageStorage.S3; + +internal sealed partial class S3ImageStorageLogger +{ + private readonly ILogger _logger; + + public S3ImageStorageLogger(ILogger logger) + { + _logger = logger; + } + + [LoggerMessage(LogLevel.Error, "Failed to upload image '{ObjectKey}' to S3. Result status code: {StatusCode}", EventId = 101)] + public partial void FailedToUploadImage(string objectKey, int statusCode); + + [LoggerMessage(LogLevel.Error, "Failed to upload image '{ObjectKey}' to S3 because of an exception.", EventId = 102)] + public partial void FailedToUploadImage(Exception exception, string objectKey); + + [LoggerMessage(LogLevel.Error, "Failed to read image '{ObjectKey}' from S3. Result status code: {StatusCode}", EventId = 103)] + public partial void FailedToReadImage(string objectKey, int statusCode); + + [LoggerMessage(LogLevel.Error, "Failed to delete image '{ObjectKey}' from S3 because of an exception.", EventId = 104)] + public partial void FailedToDeleteImage(Exception exception, string objectKey); +} diff --git a/src/Turnierplan.Localization/LocalizationProvider.cs b/src/Turnierplan.Localization/LocalizationProvider.cs index ba1a1e27..373a415d 100644 --- a/src/Turnierplan.Localization/LocalizationProvider.cs +++ b/src/Turnierplan.Localization/LocalizationProvider.cs @@ -8,12 +8,12 @@ namespace Turnierplan.Localization; internal sealed partial class LocalizationProvider : ILocalizationProvider { - private readonly ILogger _logger; + private readonly LocalizationProviderLogger _logger; private readonly Dictionary _cache = []; public LocalizationProvider(ILogger logger) { - _logger = logger; + _logger = new LocalizationProviderLogger(logger); LoadLanguages(); } @@ -47,7 +47,7 @@ private void LoadLanguages() if (stream is null) { - _logger.LogError("Stream for translations file '{LocalizationResourceName}' is null.", resourceName); + _logger.LocalizationResourceStreamIsNull(resourceName); continue; } @@ -55,7 +55,7 @@ private void LoadLanguages() if (culture is null) { - _logger.LogWarning("Could not find CultureInfo for language code '{LanguageCode}'. Falling back to 'InvariantCulture'.", languageCode); + _logger.CouldNotFindCultureInfoForLanguageCode(languageCode); culture = CultureInfo.InvariantCulture; } @@ -64,7 +64,7 @@ private void LoadLanguages() } catch (Exception ex) { - _logger.LogError(ex, "Failed to load translations for language with code '{LanguageCode}'.", languageCode); + _logger.FailedToLoadTranslationsForLanguage(ex, languageCode); } } } diff --git a/src/Turnierplan.Localization/LocalizationProviderLogger.cs b/src/Turnierplan.Localization/LocalizationProviderLogger.cs new file mode 100644 index 00000000..fb019875 --- /dev/null +++ b/src/Turnierplan.Localization/LocalizationProviderLogger.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Logging; + +namespace Turnierplan.Localization; + +internal sealed partial class LocalizationProviderLogger +{ + private readonly ILogger _logger; + + public LocalizationProviderLogger(ILogger logger) + { + _logger = logger; + } + + [LoggerMessage(LogLevel.Error, "Stream for translations file '{LocalizationResourceName}' is null.", EventId = 101)] + public partial void LocalizationResourceStreamIsNull(string localizationResourceName); + + [LoggerMessage(LogLevel.Warning, "Could not find CultureInfo for language code '{LanguageCode}'. Falling back to 'InvariantCulture'.", EventId = 102)] + public partial void CouldNotFindCultureInfoForLanguageCode(string languageCode); + + [LoggerMessage(LogLevel.Error, "Failed to load translations for language with code '{LanguageCode}'.", EventId = 103)] + public partial void FailedToLoadTranslationsForLanguage(Exception exception, string languageCode); +}