refactor(di): migrate services layer to ILogger<T> (DI-4 Slice A)
MessageStore, MessageEnumerator, MessageManager, AutoTellTabsService
move from Plugin.LogProxy / IPluginLogProxy onto
Microsoft.Extensions.Logging.ILogger<T> via constructor injection.
MessageStore additionally takes ILoggerFactory so it can build a
per-instance ILogger<MessageEnumerator> at each of the five reader-
spawning sites; the enumerator is not a container singleton.
PluginHostFactory's MessageManager and AutoTellTabsService factory
lambdas grow to resolve the new logger args; everything else stays in
place.
Site-level migration in the four files:
- MessageStore: 12 calls, _logger field IPluginLogProxy -> ILogger<MessageStore>
- MessageManager: 7 Plugin.LogProxy.* sites, new _logger field
- AutoTellTabsService: 9 Plugin.LogProxy.* sites, new _logger field
Plus a pre-existing template bug surfaced by CA2017: a LogDebug call
in AutoTellTabsService used "{tab.Name}" with no `$` prefix, which
landed in xllog as literal text under Plugin.LogProxy; ILogger now
reads that as a structured placeholder, so the call was promoted to
proper structured logging with tab.Name passed as a parameter.
This commit is contained in:
+46
-21
@@ -9,6 +9,7 @@ using MessagePack;
|
||||
using MessagePack.Formatters;
|
||||
using MessagePack.Resolvers;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Encoding = System.Text.Encoding;
|
||||
|
||||
namespace HellionChat;
|
||||
@@ -179,7 +180,8 @@ internal class MessageStore : IDisposable
|
||||
}
|
||||
|
||||
private readonly IPlatformUtil _platformUtil;
|
||||
private readonly IPluginLogProxy _logger;
|
||||
private readonly ILogger<MessageStore> _logger;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
// Readiness gate for the FTS5 full-text index. Volatile so the DbViewer's
|
||||
// per-frame IsFtsIndexBuilt read sees the flip the moment the bulk-insert
|
||||
@@ -197,11 +199,17 @@ internal class MessageStore : IDisposable
|
||||
// own SqliteConnection via OpenSecondaryConnection.
|
||||
private readonly object _readLock = new();
|
||||
|
||||
internal MessageStore(string dbPath, IPlatformUtil platformUtil, IPluginLogProxy logger)
|
||||
internal MessageStore(
|
||||
string dbPath,
|
||||
IPlatformUtil platformUtil,
|
||||
ILogger<MessageStore> logger,
|
||||
ILoggerFactory loggerFactory
|
||||
)
|
||||
{
|
||||
DbPath = dbPath;
|
||||
_platformUtil = platformUtil;
|
||||
_logger = logger;
|
||||
_loggerFactory = loggerFactory;
|
||||
Connection = Connect();
|
||||
Migrate();
|
||||
InitFtsReadyCache();
|
||||
@@ -246,7 +254,7 @@ internal class MessageStore : IDisposable
|
||||
conn.Open();
|
||||
ApplyPragmas(conn);
|
||||
connectSw.Stop();
|
||||
_logger.Information($"MessageStore.Connect took {connectSw.ElapsedMilliseconds}ms");
|
||||
_logger.LogInformation($"MessageStore.Connect took {connectSw.ElapsedMilliseconds}ms");
|
||||
return conn;
|
||||
}
|
||||
|
||||
@@ -290,12 +298,12 @@ internal class MessageStore : IDisposable
|
||||
migration();
|
||||
|
||||
migrateSw.Stop();
|
||||
_logger.Information($"MessageStore.Migrate took {migrateSw.ElapsedMilliseconds}ms");
|
||||
_logger.LogInformation($"MessageStore.Migrate took {migrateSw.ElapsedMilliseconds}ms");
|
||||
}
|
||||
|
||||
private void Migrate0()
|
||||
{
|
||||
_logger.Information("Running migration 0: Creating tables");
|
||||
_logger.LogInformation("Running migration 0: Creating tables");
|
||||
Connection.Execute(
|
||||
@"
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
@@ -322,7 +330,7 @@ internal class MessageStore : IDisposable
|
||||
|
||||
private void Migrate1()
|
||||
{
|
||||
_logger.Information("Running migration 1: Adding Deleted column");
|
||||
_logger.LogInformation("Running migration 1: Adding Deleted column");
|
||||
Connection.Execute(
|
||||
@"
|
||||
ALTER TABLE messages ADD COLUMN Deleted BOOLEAN NOT NULL DEFAULT false;
|
||||
@@ -334,7 +342,7 @@ internal class MessageStore : IDisposable
|
||||
|
||||
private void Migrate2()
|
||||
{
|
||||
_logger.Information("Running migration 2: Adding Channel generated column");
|
||||
_logger.LogInformation("Running migration 2: Adding Channel generated column");
|
||||
Connection.Execute(
|
||||
@"
|
||||
ALTER TABLE messages ADD COLUMN Channel INTEGER GENERATED ALWAYS AS (Code & 0x7f) VIRTUAL;
|
||||
@@ -362,13 +370,15 @@ internal class MessageStore : IDisposable
|
||||
|
||||
private void Migrate3()
|
||||
{
|
||||
_logger.Information("Running migration 3: Fix log kinds to fit the new format");
|
||||
_logger.LogInformation("Running migration 3: Fix log kinds to fit the new format");
|
||||
|
||||
// Recovery for partially-applied Migrate3: schema already in target
|
||||
// shape but user_version was never bumped -- just record and exit.
|
||||
if (ColumnExists("messages", "ChatType") && !ColumnExists("messages", "Code"))
|
||||
{
|
||||
_logger.Information("Migration 3: schema already migrated, only bumping user_version");
|
||||
_logger.LogInformation(
|
||||
"Migration 3: schema already migrated, only bumping user_version"
|
||||
);
|
||||
SetMigrationVersion(3);
|
||||
return;
|
||||
}
|
||||
@@ -398,7 +408,7 @@ internal class MessageStore : IDisposable
|
||||
|
||||
private void Migrate4()
|
||||
{
|
||||
_logger.Information("Running migration 4: Add FTS5 virtual table for full-text search");
|
||||
_logger.LogInformation("Running migration 4: Add FTS5 virtual table for full-text search");
|
||||
|
||||
// Standalone FTS5 table (no content='messages' linking, no content_rowid).
|
||||
// messages.Id is BLOB-PK (Guid), which is incompatible with FTS5's
|
||||
@@ -422,7 +432,7 @@ internal class MessageStore : IDisposable
|
||||
|
||||
private void SetMigrationVersion(int version)
|
||||
{
|
||||
_logger.Information($"Setting version {version}");
|
||||
_logger.LogInformation($"Setting version {version}");
|
||||
using var cmd = Connection.CreateCommand();
|
||||
// PRAGMA does not accept SQLite parameter bindings; version is a
|
||||
// compile-time int from the migration sequence, never user input.
|
||||
@@ -837,7 +847,7 @@ internal class MessageStore : IDisposable
|
||||
// Privacy filter -- drop disallowed ChatTypes before they reach storage.
|
||||
if (!Plugin.Config.IsAllowedForStorage(message.Code.Type))
|
||||
{
|
||||
_logger.Verbose($"Privacy filter dropped message: ChatType={message.Code.Type}");
|
||||
_logger.LogTrace($"Privacy filter dropped message: ChatType={message.Code.Type}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -941,7 +951,10 @@ internal class MessageStore : IDisposable
|
||||
if (to is not null)
|
||||
cmd.Parameters.AddWithValue("$To", to.Value.ToUnixTimeMilliseconds());
|
||||
|
||||
return new MessageEnumerator(cmd.ExecuteReader(), _logger);
|
||||
return new MessageEnumerator(
|
||||
cmd.ExecuteReader(),
|
||||
_loggerFactory.CreateLogger<MessageEnumerator>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -993,7 +1006,10 @@ internal class MessageStore : IDisposable
|
||||
|
||||
cmd.Parameters.AddWithValue("$Count", count);
|
||||
|
||||
return new MessageEnumerator(cmd.ExecuteReader(), _logger);
|
||||
return new MessageEnumerator(
|
||||
cmd.ExecuteReader(),
|
||||
_loggerFactory.CreateLogger<MessageEnumerator>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1033,7 +1049,10 @@ internal class MessageStore : IDisposable
|
||||
cmd.Parameters.AddWithValue("$TellOutgoing", (int)ChatType.TellOutgoing);
|
||||
|
||||
var collected = new List<Message>();
|
||||
using var enumerator = new MessageEnumerator(cmd.ExecuteReader(), _logger);
|
||||
using var enumerator = new MessageEnumerator(
|
||||
cmd.ExecuteReader(),
|
||||
_loggerFactory.CreateLogger<MessageEnumerator>()
|
||||
);
|
||||
foreach (var message in enumerator)
|
||||
{
|
||||
if (!ChunkUtil.MatchesSender(message, senderName, senderWorld))
|
||||
@@ -1145,7 +1164,10 @@ internal class MessageStore : IDisposable
|
||||
((DateTimeOffset)before).ToUnixTimeMilliseconds()
|
||||
);
|
||||
|
||||
return new MessageEnumerator(cmd.ExecuteReader(), _logger);
|
||||
return new MessageEnumerator(
|
||||
cmd.ExecuteReader(),
|
||||
_loggerFactory.CreateLogger<MessageEnumerator>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1198,7 +1220,10 @@ internal class MessageStore : IDisposable
|
||||
cmd.Parameters.AddWithValue("$Offset", DbViewer.RowPerPage * page);
|
||||
cmd.Parameters.AddWithValue("$OffsetCount", DbViewer.RowPerPage);
|
||||
|
||||
return new MessageEnumerator(cmd.ExecuteReader(), _logger);
|
||||
return new MessageEnumerator(
|
||||
cmd.ExecuteReader(),
|
||||
_loggerFactory.CreateLogger<MessageEnumerator>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1219,14 +1244,14 @@ internal class MessageStore : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
internal class MessageEnumerator(DbDataReader reader, IPluginLogProxy logger)
|
||||
internal class MessageEnumerator(DbDataReader reader, ILogger<MessageEnumerator> logger)
|
||||
: IEnumerable<Message>,
|
||||
IDisposable,
|
||||
IAsyncDisposable
|
||||
{
|
||||
private const int MaxErrorLogs = 10;
|
||||
|
||||
private readonly IPluginLogProxy _logger = logger;
|
||||
private readonly ILogger<MessageEnumerator> _logger = logger;
|
||||
private readonly List<Guid> FailedIds = [];
|
||||
private int FailedCount;
|
||||
public bool DidError => FailedCount > 0;
|
||||
@@ -1247,10 +1272,10 @@ internal class MessageEnumerator(DbDataReader reader, IPluginLogProxy logger)
|
||||
catch (Exception e)
|
||||
{
|
||||
if (FailedCount < MaxErrorLogs)
|
||||
_logger.Error($"Exception while reading message '{id}' from database: {e}");
|
||||
_logger.LogError($"Exception while reading message '{id}' from database: {e}");
|
||||
FailedCount++;
|
||||
if (FailedCount == MaxErrorLogs)
|
||||
_logger.Error("Further parsing errors will not be logged");
|
||||
_logger.LogError("Further parsing errors will not be logged");
|
||||
if (id != Guid.Empty)
|
||||
FailedIds.Add(id);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user