Trace.CorrelationManager.LogicalOperationStack
ermöglicht die Verwendung verschachtelter logischer Operationsbezeichner, bei denen der häufigste Fall die Protokollierung (NDC) ist. Sollte es noch mit async-await
funktionieren?Ist LogicalOperationStack mit async in .Net kompatibel?
Hier ist ein einfaches Beispiel LogicalFlow
mit denen über die LogicalOperationStack
meine einfache Wrapper ist:
private static void Main() => OuterOperationAsync().GetAwaiter().GetResult();
private static async Task OuterOperationAsync()
{
Console.WriteLine(LogicalFlow.CurrentOperationId);
using (LogicalFlow.StartScope())
{
Console.WriteLine("\t" + LogicalFlow.CurrentOperationId);
await InnerOperationAsync();
Console.WriteLine("\t" + LogicalFlow.CurrentOperationId);
await InnerOperationAsync();
Console.WriteLine("\t" + LogicalFlow.CurrentOperationId);
}
Console.WriteLine(LogicalFlow.CurrentOperationId);
}
private static async Task InnerOperationAsync()
{
using (LogicalFlow.StartScope())
{
await Task.Delay(100);
}
}
LogicalFlow
:
public static class LogicalFlow
{
public static Guid CurrentOperationId =>
Trace.CorrelationManager.LogicalOperationStack.Count > 0
? (Guid) Trace.CorrelationManager.LogicalOperationStack.Peek()
: Guid.Empty;
public static IDisposable StartScope()
{
Trace.CorrelationManager.StartLogicalOperation();
return new Stopper();
}
private static void StopScope() =>
Trace.CorrelationManager.StopLogicalOperation();
private class Stopper : IDisposable
{
private bool _isDisposed;
public void Dispose()
{
if (!_isDisposed)
{
StopScope();
_isDisposed = true;
}
}
}
}
Ausgang:
00000000-0000-0000-0000-000000000000
49985135-1e39-404c-834a-9f12026d9b65
54674452-e1c5-4b1b-91ed-6bd6ea725b98
c6ec00fd-bff8-4bde-bf70-e073b6714ae5
54674452-e1c5-4b1b-91ed-6bd6ea725b98
Die spezifischen Werte nicht wirklich wichtig, aber wie ich es verstehe, sowohl die äußere l inines sollte Guid.Empty
(d. h. 00000000-0000-0000-0000-000000000000
) und die inneren Linien sollten den gleichen Guid
Wert zeigen.
Sie könnten sagen, dass LogicalOperationStack
eine Stack
verwendet, die nicht Thread-sicher ist und deshalb die Ausgabe falsch ist. Aber während das in der Regel stimmt, in diesem Fall es nie mehr als ein einzelner Thread die LogicalOperationStack
zugleich Zugriff auf (jeder async
Betrieb abgewartet, wenn sie aufgerufen und keine Verwendung von Kombinatoren wie Task.WhenAll
)
Das Problem ist, dass LogicalOperationStack
in der CallContext
gespeichert ist, die ein Copy-on-Write-Verhalten aufweist. Das heißt, solange Sie nicht explizit etwas in CallContext
setzen (und Sie nicht, wenn Sie zu einem vorhandenen Stapel mit StartLogicalOperation
hinzufügen) verwenden Sie den übergeordneten Kontext und nicht Ihren eigenen.
Dies kann durch einfaches Setzen von alles in die CallContext
vor dem Hinzufügen zu dem vorhandenen Stapel angezeigt werden. Zum Beispiel, wenn wir StartScope
dazu geändert:
public static IDisposable StartScope()
{
CallContext.LogicalSetData("Bar", "Arnon");
Trace.CorrelationManager.StartLogicalOperation();
return new Stopper();
}
Die Ausgabe lautet:
00000000-0000-0000-0000-000000000000
fdc22318-53ef-4ae5-83ff-6c3e3864e37a
fdc22318-53ef-4ae5-83ff-6c3e3864e37a
fdc22318-53ef-4ae5-83ff-6c3e3864e37a
00000000-0000-0000-0000-000000000000
Hinweis: Ich bin nicht jemand darauf hindeutet, dies tatsächlich tun. Die wirkliche praktische Lösung wäre, eine ImmutableStack
anstelle der LogicalOperationStack
zu verwenden, da es sowohl threadsicher als auch unveränderlich ist, wenn Sie Pop
anrufen, erhalten Sie eine neue ImmutableStack
zurück, die Sie dann wieder in die CallContext
setzen müssen. Eine vollständige Umsetzung ist als Antwort auf diese Frage zur Verfügung: Tracking c#/.NET tasks flow
So sollte LogicalOperationStack
Arbeit mit async
und es ist nur ein Fehler? Ist LogicalOperationStack
einfach nicht für die async
Welt gedacht? Oder fehlt mir etwas?
aktualisieren: Task.Delay
Verwendung ist offenbar verwirrend, da es System.Threading.Timer
welche captures the ExecutionContext
internally verwendet. Die Verwendung von await Task.Yield();
anstelle von await Task.Delay(100);
macht das Beispiel leichter verständlich.
Möglicherweise ein Duplikat: [Ist CorrelationManager.LogicalOperationStack kompatibel mit Parallel.For, Aufgaben, Themen, usw.] (http://stackoverflow.com/questions/4729479/is-correlationmanager-logicaloperationstack-compatible-with-parallel -für-Aufgaben) – Noseratio
@Noseratio nein. Es geht nicht um 'async-await', sondern um einen bereits behobenen Fehler. – i3arnon
Einverstanden, kein Betrogener. Haben Sie überprüft, ob es im selben Thread wie erwartet funktioniert, z. Wenn Sie 'warten Task.Delay (100)' mit 'Task.Delay (100) .Wait()' ersetzen? – Noseratio