Added logging.
This commit is contained in:
51
JSMR.Application/Logging/CriteriaLoggingExtensions.cs
Normal file
51
JSMR.Application/Logging/CriteriaLoggingExtensions.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using JSMR.Application.Circles.Queries.Search;
|
||||
using JSMR.Application.Creators.Queries.Search.Contracts;
|
||||
using JSMR.Application.Tags.Queries.Search.Contracts;
|
||||
using JSMR.Application.VoiceWorks.Queries.Search;
|
||||
|
||||
namespace JSMR.Application.Logging;
|
||||
|
||||
public static class CriteriaLoggingExtensions
|
||||
{
|
||||
//public static object ToLogObject(this VoiceWorkSearchCriteria criteria)
|
||||
//{
|
||||
// return new LogObjectBuilder()
|
||||
// .AddIfNotEmpty("Keywords", criteria.Keywords)
|
||||
// .AddIfNotEmpty("Title", criteria.Title)
|
||||
// .AddIfNotEmpty("Circle", criteria.Circle)
|
||||
// .Add("Locale", criteria.Locale)
|
||||
// .AddIfNotEmpty("AgeRatings", criteria.AgeRatings)
|
||||
// .AddIfNotEmpty("Languages", criteria.SupportedLanguages)
|
||||
// .AddIfNotEmpty("TagIds", criteria.TagIds, preview: 5)
|
||||
// .AddIfNotEmpty("CreatorIds", criteria.CreatorIds, preview: 5)
|
||||
// .Add("IncludeAllTags", criteria.IncludeAllTags ? true : null)
|
||||
// .Add("IncludeAllCreators", criteria.IncludeAllCreators ? true : null)
|
||||
// .Add("MinDownloads", criteria.MinDownloads)
|
||||
// .Add("MaxDownloads", criteria.MaxDownloads)
|
||||
// .Add("ReleaseDateStart", criteria.ReleaseDateStart)
|
||||
// .Add("ReleaseDateEnd", criteria.ReleaseDateEnd)
|
||||
// .Build();
|
||||
//}
|
||||
|
||||
//public static object ToLogObject(this CircleSearchCriteria criteria)
|
||||
//{
|
||||
// return new LogObjectBuilder()
|
||||
// .AddIfNotEmpty("Name", criteria.Name)
|
||||
// .AddIfNotEmpty("Status", criteria.Status?.ToString())
|
||||
// .Build();
|
||||
//}
|
||||
|
||||
//public static object ToLogObject(this TagSearchCriteria criteria)
|
||||
//{
|
||||
// return new LogObjectBuilder()
|
||||
// .AddIfNotEmpty("Name", criteria.Name)
|
||||
// .Build();
|
||||
//}
|
||||
|
||||
//public static object ToLogObject(this CreatorSearchCriteria criteria)
|
||||
//{
|
||||
// return new LogObjectBuilder()
|
||||
// .AddIfNotEmpty("Name", criteria.Name)
|
||||
// .Build();
|
||||
//}
|
||||
}
|
||||
59
JSMR.Application/Logging/LogEvents.cs
Normal file
59
JSMR.Application/Logging/LogEvents.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace JSMR.Application.Logging;
|
||||
|
||||
public static partial class LogEvents
|
||||
{
|
||||
public static void SearchCompleted(ILogger logger, long Elapsed, int Items, int Total, int Page, int Size, object Sort, object Criteria)
|
||||
{
|
||||
logger.LogInformation(
|
||||
new EventId(1000, "SearchCompleted"),
|
||||
"Search returned {Items} items (out of {Total} total) results in {Elapsed} ms on page {Page} with Size {Size}, sorted by {@Sort}, based on the following criteria: {@Criteria}",
|
||||
Items,
|
||||
Total,
|
||||
Elapsed,
|
||||
Page,
|
||||
Size,
|
||||
Sort,
|
||||
Criteria
|
||||
);
|
||||
}
|
||||
|
||||
// Search
|
||||
[LoggerMessage(EventId = 1000, Level = LogLevel.Information,
|
||||
Message = "Search started Page={Page} Size={Size} Criteria={@Criteria}")]
|
||||
public static partial void SearchStart(ILogger logger, int Page, int Size, object Criteria);
|
||||
|
||||
//[LoggerMessage(EventId = 1001, Level = LogLevel.Information,
|
||||
// Message = "Search completed in {ElapsedMs} ms, Total={Total}")]
|
||||
//public static partial void SearchComplete(ILogger logger, long ElapsedMs, int Total);
|
||||
|
||||
[LoggerMessage(EventId = 1001, Level = LogLevel.Information,
|
||||
Message = "Search returned {Items} items (out of {Total} total) results in {Elapsed} ms on page {Page} with Size {Size}, sorted by {@Sort}, based on the following criteria: {@Criteria}")]
|
||||
public static partial void SearchComplete(ILogger logger, long Elapsed, int Items, int Total, int Page, int Size, object Sort, object Criteria);
|
||||
|
||||
[LoggerMessage(EventId = 1002, Level = LogLevel.Warning,
|
||||
Message = "External search provider timeout after {ElapsedMs} ms")]
|
||||
public static partial void ExternalSearchTimeout(ILogger logger, long ElapsedMs);
|
||||
|
||||
[LoggerMessage(EventId = 1003, Level = LogLevel.Warning,
|
||||
Message = "Slow Search Detected: Elapsed {ElapsedMs} ms (threshold {ThresholdMs} ms) Path={Path}")]
|
||||
public static partial void SlowSearchDetected(ILogger logger, long ElapsedMs, long ThresholdMs, string Path);
|
||||
|
||||
[LoggerMessage(EventId = 1004, Level = LogLevel.Error,
|
||||
Message = "Very Slow Search Detected: Elapsed {ElapsedMs} ms (threshold {ThresholdMs} ms) Path={Path}")]
|
||||
public static partial void VerySlowSearchDetected(ILogger logger, long ElapsedMs, long ThresholdMs, string Path);
|
||||
|
||||
// Worker scan batch
|
||||
[LoggerMessage(EventId = 2000, Level = LogLevel.Information,
|
||||
Message = "Scan batch {BatchId} started")]
|
||||
public static partial void ScanBatchStart(ILogger logger, string BatchId);
|
||||
|
||||
[LoggerMessage(EventId = 2001, Level = LogLevel.Information,
|
||||
Message = "Scan batch {BatchId} completed in {ElapsedMs} ms. Processed={Processed} Failures={Failures}")]
|
||||
public static partial void ScanBatchComplete(ILogger logger, string BatchId, int Processed, int Failures, long ElapsedMs);
|
||||
|
||||
[LoggerMessage(EventId = 2002, Level = LogLevel.Error,
|
||||
Message = "Scan batch {BatchId} failed")]
|
||||
public static partial void ScanBatchFailed(ILogger logger, string BatchId, Exception ex);
|
||||
}
|
||||
36
JSMR.Application/Logging/LogObjectBuilder.cs
Normal file
36
JSMR.Application/Logging/LogObjectBuilder.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace JSMR.Application.Logging;
|
||||
|
||||
public sealed class LogObjectBuilder
|
||||
{
|
||||
private readonly Dictionary<string, object> _d = [];
|
||||
|
||||
public LogObjectBuilder Add(string key, object? value)
|
||||
{
|
||||
if (value is null)
|
||||
return this;
|
||||
|
||||
_d[key] = value;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public LogObjectBuilder AddIfNotEmpty(string key, string? value)
|
||||
=> string.IsNullOrWhiteSpace(value) ? this : Add(key, value);
|
||||
|
||||
public LogObjectBuilder AddIfNotEmpty<T>(string key, IReadOnlyCollection<T>? value, int? preview = null)
|
||||
{
|
||||
if (value is null || value.Count == 0)
|
||||
return this;
|
||||
|
||||
if (preview is null || value.Count <= preview)
|
||||
return Add(key, value);
|
||||
|
||||
// Store small preview + count so logs stay compact
|
||||
return Add(key, new { Preview = value.Take(preview.Value), value.Count });
|
||||
}
|
||||
|
||||
public LogObjectBuilder AddIfNotDefault<T>(string key, T value) where T : struct, IEquatable<T>
|
||||
=> value.Equals(default) ? this : Add(key, value);
|
||||
|
||||
public object Build() => _d;
|
||||
}
|
||||
87
JSMR.Application/Logging/LoggingExtensions.cs
Normal file
87
JSMR.Application/Logging/LoggingExtensions.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using JSMR.Application.Circles.Queries.Search;
|
||||
using JSMR.Application.Common.Search;
|
||||
using JSMR.Application.Creators.Queries.Search.Contracts;
|
||||
using JSMR.Application.Tags.Queries.Search.Contracts;
|
||||
using JSMR.Application.VoiceWorks.Queries.Search;
|
||||
|
||||
namespace JSMR.Application.Logging;
|
||||
|
||||
public static class LoggingExtensions
|
||||
{
|
||||
public static object ToLogObject<TCriteria, TSortField>(this SearchOptions<TCriteria, TSortField> searchOptions)
|
||||
where TCriteria : new()
|
||||
where TSortField : struct, Enum
|
||||
{
|
||||
return new LogObjectBuilder()
|
||||
.Add("PageNumber", searchOptions.PageNumber)
|
||||
.Add("PageSize", searchOptions.PageSize)
|
||||
.Add("Criteria", MapCriteriaToLogObject(searchOptions.Criteria))
|
||||
//.AddIfNotEmpty("Sort", searchOptions.SortOptions.ToLogObject())
|
||||
.Build();
|
||||
}
|
||||
|
||||
private static object MapCriteriaToLogObject<TCriteria>(TCriteria criteria) => criteria switch
|
||||
{
|
||||
VoiceWorkSearchCriteria voiceWorkSearchCriteria => voiceWorkSearchCriteria.ToLogObject(),
|
||||
CircleSearchCriteria circleSearchCriteria => circleSearchCriteria.ToLogObject(),
|
||||
TagSearchCriteria tagSearchCriteria => tagSearchCriteria.ToLogObject(),
|
||||
CreatorSearchCriteria creatorSearchCriteria => creatorSearchCriteria.ToLogObject(),
|
||||
_ => criteria!
|
||||
};
|
||||
|
||||
public record SortLog(int Index, string Field, string Direction);
|
||||
|
||||
public static object ToLogObject<TSortField>(this IEnumerable<SortOption<TSortField>> sort)
|
||||
where TSortField : struct, Enum
|
||||
{
|
||||
return sort.Select((sortOption, index) =>
|
||||
new {
|
||||
Index = index,
|
||||
Field = sortOption.Field.ToString(),
|
||||
Direction = sortOption.Direction.ToString()
|
||||
}
|
||||
).ToList();
|
||||
}
|
||||
|
||||
public static object ToLogObject(this VoiceWorkSearchCriteria criteria)
|
||||
{
|
||||
return new LogObjectBuilder()
|
||||
.AddIfNotEmpty("Keywords", criteria.Keywords)
|
||||
.AddIfNotEmpty("Title", criteria.Title)
|
||||
.AddIfNotEmpty("Circle", criteria.Circle)
|
||||
.Add("Locale", criteria.Locale)
|
||||
.AddIfNotEmpty("AgeRatings", criteria.AgeRatings)
|
||||
.AddIfNotEmpty("Languages", criteria.SupportedLanguages)
|
||||
.AddIfNotEmpty("TagIds", criteria.TagIds, preview: 5)
|
||||
.AddIfNotEmpty("CreatorIds", criteria.CreatorIds, preview: 5)
|
||||
.Add("IncludeAllTags", criteria.IncludeAllTags ? true : null)
|
||||
.Add("IncludeAllCreators", criteria.IncludeAllCreators ? true : null)
|
||||
.Add("MinDownloads", criteria.MinDownloads)
|
||||
.Add("MaxDownloads", criteria.MaxDownloads)
|
||||
.Add("ReleaseDateStart", criteria.ReleaseDateStart)
|
||||
.Add("ReleaseDateEnd", criteria.ReleaseDateEnd)
|
||||
.Build();
|
||||
}
|
||||
|
||||
public static object ToLogObject(this CircleSearchCriteria criteria)
|
||||
{
|
||||
return new LogObjectBuilder()
|
||||
.AddIfNotEmpty("Name", criteria.Name)
|
||||
.AddIfNotEmpty("Status", criteria.Status?.ToString())
|
||||
.Build();
|
||||
}
|
||||
|
||||
public static object ToLogObject(this TagSearchCriteria criteria)
|
||||
{
|
||||
return new LogObjectBuilder()
|
||||
.AddIfNotEmpty("Name", criteria.Name)
|
||||
.Build();
|
||||
}
|
||||
|
||||
public static object ToLogObject(this CreatorSearchCriteria criteria)
|
||||
{
|
||||
return new LogObjectBuilder()
|
||||
.AddIfNotEmpty("Name", criteria.Name)
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user