feat(site-runtime): ScriptLogger (Log global), TraceStepScope, TraceCapturingLogger (D4-D7)
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Scripts;
|
||||
|
||||
/// <summary>
|
||||
/// Flow Debugger step logs (design 2026-09-09 D6): decorates the script context's
|
||||
/// <see cref="ILogger"/> while a run is traced.
|
||||
///
|
||||
/// <para>Forwards EVERYTHING to the inner logger unchanged (the node's log file is unaffected by
|
||||
/// tracing), and additionally copies <see cref="LogLevel.Information"/> and above into
|
||||
/// <c>ScriptLog</c> rows with source <c>runtime</c>, attributed to the ambient
|
||||
/// <see cref="TraceStepScope"/>. This is how the warnings the runtime logs on the script's behalf —
|
||||
/// inside <c>SetAttribute</c>, <c>WriteBatchAndWait</c>, <c>WaitInternal</c>, the sync
|
||||
/// <c>ExternalSystem.Call</c> — reach the Flow Debugger without editing each call site.</para>
|
||||
///
|
||||
/// <para>Debug and Trace are deliberately NOT captured (internal chatter, design D6), and the
|
||||
/// per-run line cap of the sink applies to what is.</para>
|
||||
///
|
||||
/// <para>Installed only when the run is traced, so an untraced run keeps the bare inner logger.</para>
|
||||
/// </summary>
|
||||
/// <param name="inner">The node logger every entry is forwarded to.</param>
|
||||
/// <param name="sink">The run's <see cref="ScriptLogger"/>, which owns the caps and the row emitter.</param>
|
||||
public sealed class TraceCapturingLogger(ILogger inner, ScriptLogger sink) : ILogger
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => inner.BeginScope(state);
|
||||
|
||||
/// <summary>
|
||||
/// Information and above are always enabled — they are trace data even when the node's minimum
|
||||
/// level is Warning. Below Information the inner logger decides.
|
||||
/// </summary>
|
||||
/// <param name="level">The level being tested.</param>
|
||||
/// <returns><see langword="true"/> when the entry should be produced.</returns>
|
||||
public bool IsEnabled(LogLevel level) => level >= LogLevel.Information || inner.IsEnabled(level);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Log<TState>(LogLevel level, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
// Forward first, and only if the inner logger actually wants it: IsEnabled above lies
|
||||
// upward for trace capture, so the node's own minimum level must be re-checked here.
|
||||
if (inner.IsEnabled(level))
|
||||
{
|
||||
inner.Log(level, eventId, state, exception, formatter);
|
||||
}
|
||||
|
||||
if (level < LogLevel.Information || level == LogLevel.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var name = level switch
|
||||
{
|
||||
LogLevel.Information => "info",
|
||||
LogLevel.Warning => "warn",
|
||||
_ => "error",
|
||||
};
|
||||
|
||||
sink.Runtime(name, formatter(state, exception), exception);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user