mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-05-08 23:54:10 -04:00
Merge pull request #1727 from rmcrackan/rmcrackan/log-init-db
Log initial db state, esp to capture books in trash
This commit is contained in:
@@ -225,6 +225,7 @@ public static class LibationScaffolding
|
||||
private static void configureLogging(Configuration config)
|
||||
{
|
||||
config.ConfigureLogging();
|
||||
DbContexts.TryEmitPendingInitialDatabaseStatistics();
|
||||
|
||||
// capture most Console.WriteLine() and write to serilog. See below tests for details.
|
||||
// Some dependencies print helpful info via Console.WriteLine. We'd like to log it.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using DataLayer;
|
||||
using LibationFileManager;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace ApplicationServices;
|
||||
|
||||
@@ -8,6 +11,24 @@ public static class DbContexts
|
||||
{
|
||||
private static bool _sqliteDbValidated;
|
||||
|
||||
private static readonly object _initialDatabaseStatisticsCaptureLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// True after initial DB statistics were read and either written to Serilog or stored for <see cref="TryEmitPendingInitialDatabaseStatistics"/>.
|
||||
/// False if capture has not run yet or the last attempt threw (a later <see cref="GetContext"/> may retry).
|
||||
/// </summary>
|
||||
private static bool _initialDatabaseStatisticsCaptured;
|
||||
|
||||
/// <summary>Shape of the initial DB statistics log event; edit here only when changing what is logged.</summary>
|
||||
private sealed class InitialDatabaseStatistics
|
||||
{
|
||||
public required int LibraryBooksNotInTrash { get; init; }
|
||||
public required int LibraryBooksInTrash { get; init; }
|
||||
public required int BookRecords { get; init; }
|
||||
}
|
||||
|
||||
private static InitialDatabaseStatistics? _pendingInitialDbStats;
|
||||
|
||||
/// <summary>Use for fully functional context, incl. SaveChanges(). For query-only, use the other method</summary>
|
||||
public static LibationContext GetContext()
|
||||
{
|
||||
@@ -25,9 +46,59 @@ public static class DbContexts
|
||||
_sqliteDbValidated = true;
|
||||
}
|
||||
|
||||
TryCaptureInitialDatabaseStatistics(context);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private static void TryCaptureInitialDatabaseStatistics(LibationContext context)
|
||||
{
|
||||
lock (_initialDatabaseStatisticsCaptureLock)
|
||||
{
|
||||
if (_initialDatabaseStatisticsCaptured)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var (notInTrash, inTrash) = context.GetLibraryBookCountsByTrashFlag();
|
||||
var bookRecords = context.GetBookCount();
|
||||
|
||||
var stats = new InitialDatabaseStatistics
|
||||
{
|
||||
LibraryBooksNotInTrash = notInTrash,
|
||||
LibraryBooksInTrash = inTrash,
|
||||
BookRecords = bookRecords,
|
||||
};
|
||||
|
||||
if (Configuration.Instance.SerilogInitialized)
|
||||
LogInitialDatabaseStatistics(stats);
|
||||
else
|
||||
_pendingInitialDbStats = stats;
|
||||
|
||||
_initialDatabaseStatisticsCaptured = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (Configuration.Instance.SerilogInitialized)
|
||||
Log.Warning(ex, "Could not capture initial database statistics");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes initial DB statistics that were captured before Serilog was configured (e.g. WinForms early library load).
|
||||
/// Call once after <see cref="Configuration.ConfigureLogging"/>.
|
||||
/// </summary>
|
||||
public static void TryEmitPendingInitialDatabaseStatistics()
|
||||
{
|
||||
var pending = Interlocked.Exchange(ref _pendingInitialDbStats, null);
|
||||
if (pending is not null)
|
||||
LogInitialDatabaseStatistics(pending);
|
||||
}
|
||||
|
||||
private static void LogInitialDatabaseStatistics(InitialDatabaseStatistics stats) =>
|
||||
Log.Logger.Information("Initial database statistics. {@DbStats}", stats);
|
||||
|
||||
/// <summary>Use for full library querying. No lazy loading</summary>
|
||||
public static List<LibraryBook> GetLibrary_Flat_NoTracking(bool includeParents = false)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,10 @@ public static class BookQueries
|
||||
.AsNoTrackingWithIdentityResolution()
|
||||
.GetBook(productId);
|
||||
|
||||
/// <summary>Total rows in <see cref="LibationContext.Books"/> (no related entities loaded).</summary>
|
||||
public static int GetBookCount(this LibationContext context)
|
||||
=> context.Books.AsNoTracking().Count();
|
||||
|
||||
public static Book? GetBook(this IQueryable<Book> books, string productId)
|
||||
=> books
|
||||
.GetBooks()
|
||||
|
||||
@@ -62,6 +62,13 @@ public static class LibraryBookQueries
|
||||
.Where(lb => lb.IsDeleted || lb.Book.ContentType == ContentType.Parent)
|
||||
.getLibrary()
|
||||
.ToList();
|
||||
|
||||
/// <summary>Counts <see cref="LibraryBook"/> rows by <see cref="LibraryBook.IsDeleted"/> (no related entities loaded).</summary>
|
||||
public (int NotInTrash, int InTrash) GetLibraryBookCountsByTrashFlag()
|
||||
{
|
||||
var q = context.LibraryBooks.AsNoTracking();
|
||||
return (q.Count(lb => !lb.IsDeleted), q.Count(lb => lb.IsDeleted));
|
||||
}
|
||||
}
|
||||
|
||||
extension(IQueryable<LibraryBook> library)
|
||||
|
||||
Reference in New Issue
Block a user