feat(logging): add dev signature to DalamudLogger output
EUPL-1.2 reuse with attribution is valid; this commit catches the case
where attribution was stripped. Two layers of provenance markers,
combined so removing one still leaves the other.
Layer 1 (subtle, kopier-resistent):
- DalamudLogger.Log emits "[name]<U+200B>{level} message" — a
zero-width space (U+200B) between the category bracket and the
level value. Visually identical to the previous format in xllog;
a hex dump of the log file shows e2 80 8b between 5d and 7b.
Survives 1:1 code copies. A copier who reformats whitespace will
strip it, which is itself a tell (the original Lightless pattern
does not have the marker, so its absence in a port is a positive
signal of derived origin).
Layer 2 (overt, abrasiv-kopier-resistent):
- DalamudLoggingProvider's ctor emits a one-shot bootstrap line:
"HellionChat DI-Logger bootstrap v{AssemblyVersion} fingerprint={hash}".
Visible in xllog as the first plugin INFO line. Fingerprint is the
first 8 hex chars of SHA256("HellionForgeBronzeC2410C-{version}"),
so the same plugin version always produces the same marker (handy
for cross-checking). A copier who keeps the banner is plagiarising
in plain sight; a copier who rips it out has to find every
reference inside DalamudLoggingProvider — quite explicit work.
Hellion Forge Bronze #C2410C is the branding-anchor const used by
the fingerprint, so the marker stays meaningful even if the plugin
version cycles.
This commit is contained in:
@@ -33,14 +33,19 @@ internal sealed class DalamudLogger : ILogger
|
|||||||
if (!IsEnabled(logLevel))
|
if (!IsEnabled(logLevel))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// The U+200B zero-width space between the bracket and the level
|
||||||
|
// value is a quiet provenance marker. The Hellion DI-Logger format
|
||||||
|
// is byte-distinguishable from any other port of this pattern even
|
||||||
|
// after the visible text is identical. EUPL-1.2 reuse stays valid;
|
||||||
|
// attribution traces stay possible.
|
||||||
if ((int)logLevel <= (int)LogLevel.Information)
|
if ((int)logLevel <= (int)LogLevel.Information)
|
||||||
{
|
{
|
||||||
_pluginLog.Information($"[{_name}]{{{(int)logLevel}}} {state}");
|
_pluginLog.Information($"[{_name}]{{{(int)logLevel}}} {state}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.Append($"[{_name}]{{{(int)logLevel}}} {state} {exception?.Message}");
|
sb.Append($"[{_name}]{{{(int)logLevel}}} {state} {exception?.Message}");
|
||||||
if (!string.IsNullOrWhiteSpace(exception?.StackTrace))
|
if (!string.IsNullOrWhiteSpace(exception?.StackTrace))
|
||||||
sb.AppendLine(exception.StackTrace);
|
sb.AppendLine(exception.StackTrace);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
@@ -7,6 +10,11 @@ namespace HellionChat.Infrastructure.Logging;
|
|||||||
[ProviderAlias("Dalamud")]
|
[ProviderAlias("Dalamud")]
|
||||||
public sealed class DalamudLoggingProvider : ILoggerProvider
|
public sealed class DalamudLoggingProvider : ILoggerProvider
|
||||||
{
|
{
|
||||||
|
// Hellion Forge Bronze (#C2410C). Stable marker that the build pipeline
|
||||||
|
// never touches; mixed into the bootstrap fingerprint so the banner stays
|
||||||
|
// distinguishable from any 1:1 port of the Lightless pattern.
|
||||||
|
private const string HellionMarker = "HellionForgeBronzeC2410C";
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, DalamudLogger> _loggers = new(
|
private readonly ConcurrentDictionary<string, DalamudLogger> _loggers = new(
|
||||||
StringComparer.OrdinalIgnoreCase
|
StringComparer.OrdinalIgnoreCase
|
||||||
);
|
);
|
||||||
@@ -16,6 +24,33 @@ public sealed class DalamudLoggingProvider : ILoggerProvider
|
|||||||
public DalamudLoggingProvider(IPluginLog pluginLog)
|
public DalamudLoggingProvider(IPluginLog pluginLog)
|
||||||
{
|
{
|
||||||
_pluginLog = pluginLog;
|
_pluginLog = pluginLog;
|
||||||
|
EmitBootstrapBanner();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runs once per plugin load (the provider is a container singleton). The
|
||||||
|
// banner is intentionally visible in xllog: anyone copying the
|
||||||
|
// DalamudLogger trio without re-branding will keep emitting "HellionChat
|
||||||
|
// DI-Logger bootstrap …", which makes uncredited reuse trivial to spot.
|
||||||
|
// EUPL-1.2 reuse with attribution stays valid; this only catches the
|
||||||
|
// case where attribution was stripped.
|
||||||
|
private void EmitBootstrapBanner()
|
||||||
|
{
|
||||||
|
var version =
|
||||||
|
typeof(DalamudLoggingProvider).Assembly.GetName().Version?.ToString() ?? "0.0.0";
|
||||||
|
var fingerprint = ComputeFingerprint(version);
|
||||||
|
_pluginLog.Information(
|
||||||
|
$"HellionChat DI-Logger bootstrap v{version} fingerprint={fingerprint}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ComputeFingerprint(string version)
|
||||||
|
{
|
||||||
|
var seed = Encoding.UTF8.GetBytes($"{HellionMarker}-{version}");
|
||||||
|
var hash = SHA256.HashData(seed);
|
||||||
|
var sb = new StringBuilder(8);
|
||||||
|
for (var i = 0; i < 4; i++)
|
||||||
|
sb.Append(hash[i].ToString("x2"));
|
||||||
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ILogger CreateLogger(string categoryName)
|
public ILogger CreateLogger(string categoryName)
|
||||||
|
|||||||
Reference in New Issue
Block a user