diff --git a/docs/azurekitlibrary.md b/docs/azurekitlibrary.md index 70824236..e6064269 100644 --- a/docs/azurekitlibrary.md +++ b/docs/azurekitlibrary.md @@ -9,7 +9,8 @@ The `PopForums.AzureKit` library makes it possible to wire up the following scen * Using Azure Storage queues and Functions to queue work for search indexing, emailing and scoring game award calculation * Using Azure Storage for image uploads * Using Azure Search -* Using Azure storage for hosting uploaded images in posts +* Using Azure Storage for hosting uploaded images in posts +* Using Azure Table Storage for error logging You don't need to use the AzureKit components to run in an Azure App Service. These components are intended for making scale-out possible. The web app can run self-contained in a single node on an Azure App Service without these optional bits. @@ -213,4 +214,12 @@ services.AddPopForumsAzureBlobStorageForPostImages(); ``` It's important to note that `PopForums.AzureKit.Functions` is already wired to use the blob storage `IPostImageRepository` version, because it's assumed that if you're already using an Azure queue, you also have a storage account. -Another thing to keep in mind is that if you're working locally, and your Azurite instance doesn't have `https` configured, it will break because most browsers do not allow non-`https` images to appear in a secure page. The simplest work around for this is not to install a local certificate, but to change your launch settings for the web app to not run on `https`. \ No newline at end of file +Another thing to keep in mind is that if you're working locally, and your Azurite instance doesn't have `https` configured, it will break because most browsers do not allow non-`https` images to appear in a secure page. The simplest work around for this is not to install a local certificate, but to change your launch settings for the web app to not run on `https`. + +## Using Azure Table Storage for error logging + +You may prefer to do your error logging to Azure Table Storage instead of the SQL database. You can do this by adding one line to your `Program` file, which swaps out the SQL error repository for the table storage: +``` +services.AddPopForumsTableStorageLogging(); +``` +The one limitation here is that you can't use the admin UI to look at errors, since paging and ordering is fairly crude in table storage. For the connection string, it will use whatever is found in `PopForums:Storage:ConnectionString`, the same setting used by the image uploads. \ No newline at end of file diff --git a/docs/versionhistory.md b/docs/versionhistory.md index d660956b..a4e9d7d7 100644 --- a/docs/versionhistory.md +++ b/docs/versionhistory.md @@ -26,6 +26,7 @@ Here's a partial version history that shows how POP Forums has evolved over the * Remove composite activity feed #299 * Use fulltext editor for profile signatures #291 * Decommission email from profile #310 +* Add option to write error log to table storage #314 * Refactor: Move all of the client stuff to the `Mvc` project, ditch the npm package #285 * Refactor: Update MailKit library to v3.x #268 * Refactor: Update ImageSharp library to v2.x #267 diff --git a/src/PopForums.AzureKit.Functions/Program.cs b/src/PopForums.AzureKit.Functions/Program.cs index 6a9c1f1f..0a054df2 100644 --- a/src/PopForums.AzureKit.Functions/Program.cs +++ b/src/PopForums.AzureKit.Functions/Program.cs @@ -38,7 +38,10 @@ s.AddTransient(); s.RemoveAll(); s.AddTransient(); - + + // use Azure table storage for logging instead of database + //s.AddPopForumsTableStorageLogging(); + switch (config.SearchProvider.ToLower()) { case "elasticsearch": diff --git a/src/PopForums.AzureKit/Logging/ErrorLogRepository.cs b/src/PopForums.AzureKit/Logging/ErrorLogRepository.cs new file mode 100644 index 00000000..ea64313a --- /dev/null +++ b/src/PopForums.AzureKit/Logging/ErrorLogRepository.cs @@ -0,0 +1,83 @@ +using PopForums.Configuration; +using PopForums.Models; +using PopForums.Repositories; +using PopForums.Services; +using System.Collections.Generic; +using System.Threading.Tasks; +using System; +using Azure.Data.Tables; + +namespace PopForums.AzureKit.Logging; + +public class ErrorLogRepository : IErrorLogRepository +{ + private readonly ITenantService _tenantService; + private readonly IConfig _config; + private const string ErrorTableName = "pferrorlog"; + private TableClient _tableClient; + + public ErrorLogRepository(ITenantService tenantService, IConfig config) + { + _tenantService = tenantService; + _config = config; + } + + public async Task Create(DateTime timeStamp, string message, string stackTrace, string data, ErrorSeverity severity) + { + var tableClient = await GetTableClient(); + var errorLog = new ErrorLogEntry + { + ErrorID = 0, + TimeStamp = timeStamp, + Message = message, + StackTrace = stackTrace, + Data = data, + Severity = severity + }; + var tenantID = _tenantService.GetTenant(); + var rowKey = DateTime.UtcNow.ToString("s") + Random.Shared.Next(100000,999999); + var entry = new TableEntity($"{DateTime.UtcNow.Year}-{DateTime.UtcNow.DayOfYear.ToString("D3")}", rowKey) + { + { "TenantID", tenantID }, + { "Message", errorLog.Message }, + { "StackTrace", errorLog.StackTrace }, + { "Data", errorLog.Data }, + { "Severity", errorLog.Severity.ToString() } + }; + await tableClient.AddEntityAsync(entry); + return errorLog; + } + + private async Task GetTableClient() + { + if (_tableClient != null) + return _tableClient; + var tableClient = new TableClient(_config.StorageConnectionString, ErrorTableName); + await tableClient.CreateIfNotExistsAsync(); + _tableClient = tableClient; + return tableClient; + } + + public Task GetErrorCount() + { + return Task.FromResult(1); + } + + public Task> GetErrors(int startRow, int pageSize) + { + var entry = new ErrorLogEntry {Message = "Check table storage for errors."}; + var list = new List {entry}; + return Task.FromResult(list); + } + + public Task DeleteError(int errorID) + { + throw new NotImplementedException(); + } + + public async Task DeleteAllErrors() + { + var tableClient = new TableClient(_config.StorageConnectionString, ErrorTableName); + await tableClient.DeleteAsync(); + } +} \ No newline at end of file diff --git a/src/PopForums.AzureKit/PopForums.AzureKit.csproj b/src/PopForums.AzureKit/PopForums.AzureKit.csproj index 33269663..5d3347ed 100644 --- a/src/PopForums.AzureKit/PopForums.AzureKit.csproj +++ b/src/PopForums.AzureKit/PopForums.AzureKit.csproj @@ -21,6 +21,7 @@ + @@ -31,8 +32,4 @@ - - - - diff --git a/src/PopForums.AzureKit/ServiceCollectionExtensions.cs b/src/PopForums.AzureKit/ServiceCollectionExtensions.cs index 03a31456..0d657777 100644 --- a/src/PopForums.AzureKit/ServiceCollectionExtensions.cs +++ b/src/PopForums.AzureKit/ServiceCollectionExtensions.cs @@ -52,4 +52,10 @@ public static IServiceCollection AddPopForumsAzureBlobStorageForPostImages(this services.Replace(ServiceDescriptor.Transient()); return services; } + + public static IServiceCollection AddPopForumsTableStorageLogging(this IServiceCollection services) + { + services.Replace(ServiceDescriptor.Transient()); + return services; + } } \ No newline at end of file diff --git a/src/PopForums.Web/Program.cs b/src/PopForums.Web/Program.cs index c48f0701..f0efd81b 100644 --- a/src/PopForums.Web/Program.cs +++ b/src/PopForums.Web/Program.cs @@ -43,6 +43,9 @@ // this adds dependencies from the MVC project (and base dependencies) and sets up authentication for the forum services.AddPopForumsMvc(); +// use Azure table storage for logging instead of database +//services.AddPopForumsTableStorageLogging(); + // use Redis cache for POP Forums using AzureKit //services.AddPopForumsRedisCache();