.NET Framework Tracing-SDK
Die Ablaufverfolgung mit „ Instana “ erfolgt automatisch. Wenn Sie jedoch noch mehr Einblick in benutzerdefinierten Code, einen bestimmten Anwendungsbereich oder eine interne Komponente erhalten möchten, können Sie das „.NET Tracing SDK“ von „ Instana “ wie im Folgenden beschrieben nutzen.
SDK installieren
SDK für .NET wird als NuGet-Paket im offiziellen Feed von nuget.org zur Verfügung gestellt. Suchen Sie nach Instana.ManagedTracing.Sdk , um es zu suchen und zu Ihrem Projekt hinzuzufügen. Nach der Installation enthält Ihr Projekt zwei zusätzliche Referenzen (Instana.ManagedTracing.Sdk und Instana.ManagedTracing.Api).
Tracing für Ihren eigenen Code
In den folgenden Beispielen wird veranschaulicht, wie Sie verteilte Tracingfunktionen zu Ihrem Code hinzufügen können.
Einfache Spanne erstellen
Die einfachste Möglichkeit, einen Methodenaufruf mit einem Trace zu verfolgen, ist die Verwendung der API
CustomSpan.Create.
public void MyTracedMethod()
{
using(var span = CustomSpan.Create())
{
// your original code here
}
}
Dieser Code erstellt einen Zwischen-Span, der Ihre Methode und die darin verbrachte Zeit darstellt.
Wenn Sie anstelle einer Zwischenspanne eine Eingangs- oder Ausgangsspanne erstellen möchten, gibt es vorgefertigte APIs für
diesen Zweck (mit dem Namen CustomSpan.CreateEntry bzw. CustomSpan.CreateExit).
Einfache Spanne erstellen und Ausnahmen erfassen
Falls Sie die erstellten Spannen mit Anmerkungen zu Fehlern versehen möchten, die während der Verarbeitung des
Methodenhauptteils auftreten, können Sie dies natürlich manuell mit der API
CustomSpan.SetError wie folgt durchführen.
public void MyTracedMethod()
{
using(var span = CustomSpan.Create())
{
try
{
// your original code here
}
catch(Exception e)
{
span.SetError(e);
}
}
}
Eine einfachere-und bevorzugte-Methode ist die Verwendung der APIs CustomSpan.WrapAction oder CustomSpan.Wrap<T> .
public void MyTracedMethod()
{
using(var span = CustomSpan.Create())
{
// setting the second argument to "false" will prevent exceptions from being thrown. Instead they will be
// captured in the span and swallowed. Setting it to true will let you handle exceptions yourself.
span.WrapAction(
()=>{
// your original code here
}, true);
}
}
Wenn der Code-Block, den Sie umschließen möchten, etwas zurückgibt, das Sie für die weitere Verarbeitung benötigen, verwenden Sie stattdessen die Funktion ` APICustomSpan.Wrap<T> `.
public void MyTracedMethod()
{
using(var span = CustomSpan.Create())
{
// setting the second argument to "false" will prevent exceptions from being thrown. Instead they will be
// captured in the span and swallowed. Setting it to true will let you handle exceptions yourself.
bool result = span.Wrap<bool>(
()=>{
// your original code here
return myBooleanValue;
}, true);
}
}
Nun wissen Sie, wie Sie Eingänge, Exits und temporäre Elemente erstellen können. Wir haben auch gelernt, wie Ausnahmen entweder mit den APIs SetError oder WrapAction / Wrap<T> erfasst werden.
Daten zu Ihren Spannen hinzufügen
Eine Spanne allein besteht lediglich aus einem Timing, einem Callstack und einem Namen. Das ist eine Basis, aber in
den meisten Szenarien nicht sehr hilfreich. Sie sollten daher einige Daten zu Ihrer Spanne hinzufügen. Ein Bereich kann Data und Tagsenthalten, für die die Klasse CustomSpan einfache APIs bietet.
public void MyTracedMethod(string userName, string someSuperRelevantData)
{
using(var span = CustomSpan.Create())
{
span.SetData("username", userName);
span.SetData("relevant", someSuperRelevantData);
span.WrapAction(
()=>{
// your original code here
}, true);
}
}
Die Daten werden an das Back-End übertragen und können über die Benutzerschnittstelle als Trace heruntergeladen werden. Die hier eingegebenen Daten werden jedoch nicht in der Benutzeroberfläche von „ Instana “ angezeigt.
Tags zu Ihren Spannen hinzufügen
Anstelle von SetData können Sie auch SetTag verwenden, wobei ein Array von
Zeichenfolgen als Schlüssel verwendet wird (mit dem die Daten, die an die Spanne als Hierarchie übergeben werden, strukturiert
werden können).
public void MyTracedMethod(string userName, string someSuperRelevantData)
{
using(var span = CustomSpan.Create())
{
span.SetTag("username", userName);
span.SetTag("relevant", someSuperRelevantData);
span.WrapAction(
()=>{
// your original code here
}, true);
}
}
Die Tags werden direkt in der Ansicht 'Aufrufdetails' angezeigt und können auch in unbegrenzten Analysen gesucht werden.
Angepasste Spannen einem Service zuordnen
In den Anwendungsperspektiven von Instana sollten Sie normalerweise Ihre angepassten Spannen einem logischen Service
zuordnen. Dies ist so einfach wie das Aufrufen der API SetServicename und erfordert nur eine Zeichenfolge. Um
zwischen mit dem SDK implementierten Endpunkten zu unterscheiden, können Sie auch einen Endpunkt für eine detailliertere
Zuordnung mit der API SetEndpointName bereitstellen.
public void MyTracedMethod(string userName, string someSuperRelevantData)
{
using(var span = CustomSpan.Create())
{
span.SetServiceName("AwesomeSDKService");
span.SetEndpointName("TracingEndpoint");
.
.
.
}
}
Während Sie für jede Spanne einen Service und einen Endpunkt festlegen können, müssen Sie beachten, dass diese
Einstellungen im Falle von Spannen vom Typ INTERMEDIATE (Zwischenspannen) verworfen werden (sie werden vom letzten
vorherigen ENTRY (Eingang) übernommen).
Ergebnis Ihrer Spanne erfassen
Die Methode, die Sie instrumentieren, gibt möglicherweise einen Wert zurück. Nehmen wir an, dass dieser Wert in Ihrem Trace
hilfreich für die Fehlerbehebung wäre. Geben Sie die API SetResult an.
public bool MyTracedMethod(string userName, string someSuperRelevantData)
{
using(var span = CustomSpan.Create())
{
bool result = span.Wrap<bool>(
()=>{
// your original code here
return resultingBoolean;
}, true);
span.SetResult(result.ToString());
return result;
}
}
Spannen ineinander verschachteln
Die Verschachtelung von Spannen ist so einfach wie das Aufrufen einer Methode über eine andere Methode. Der Vollständigkeit halber wird hier ein Beispiel angegeben. Es wird angenommen, dass eine Methode als Eingang dient, während ihre untergeordnete Spanne ein temporäres Element (d. h. eine Zwischenspanne) ist.
public bool MyTracedEntryMethod(string userName, string someSuperRelevantData)
{
using(var span = CustomSpan.CreateEntry())
{
bool result = span.Wrap<bool>(
()=>{
List<string> data = this.GetSomeDataFromSomewhere();
// do some heavy processing
return theResultICameUpWith;
}, true);
span.SetResult(result.ToString());
return result;
}
}
private List<string> GetSomeDataFromSomewhere()
{
using(var span = CustomSpan.Create())
{
List<string> result = span.WrapAction(
()=>{
// read data from somewhere...
return theListICameUpWith;
}, true);
span.SetResult(result.ToString());
return result;
}
}
Das Ergebnis wäre in diesem Fall eine Eingangspanne mit einer Zwischenspanne als untergeordnetes Element. Technisch gesehen gilt keine Beschränkung für die Verschachtelungstiefe, aber Sie sollten keine Spannen in einer tiefen Rekursion erstellen.
Ein Blick auf die distributed verteilte Ablaufverfolgung
Alle Spannen, die wir bisher betrachtet haben, waren auf einen einzigen Service beschränkt. Sie bezogen sich nicht auf eine andere Komponente, wodurch auch deren Aktivität verfolgt werden würde. Um einen echten verteilten Trace über Servicegrenzen hinweg zu erreichen, müssen Sie eine bestimmte Korrelation anwenden.
Die Korrelation im Tracing beschreibt, wie die Korrelationsdaten in einem Exit-Aufruf festgelegt werden und wie Sie diese Daten wieder zurückerhalten und diesen Kontext für den Eingang bei der anderen Komponente 'fortsetzen'.
Um dies zu erreichen, verwendet CustomSpan Überladungen der Methoden CustomSpan.CreateExit und
CustomSpan.CreateEntry.
Während CustomSpan.CreateExit eine Action<string, string> als Argument verarbeiten kann, nimmt CustomSpan.CreateEntry eine Func<DistributedTraceInformation>an.
Funktionsweise
Nehmen wir an, wir haben eine Message Klasse, die wir an den aufgerufenen Remote-Dienst übergeben.
public class Message
{
public Message()
{
this.Tags = new Dictionary<string, string>();
}
public Dictionary<string, string> Tags { get; private set; }
public int Payload { get; set; }
public void AddTag(string tagName, string tagValue)
{
this.Tags.Add(tagName, tagValue);
}
}
In diesem Fall ist der relevante Teil die Methode AddTag, die zwei Zeichenfolgen übernimmt. Dies ist die
Signatur, die wir für CustomSpan.CreateExit bereitstellen müssen.
Wenn wir diese Methode beim Aufrufen von CreateExit verwenden, werden die Korrelationsdaten in unsere
Instanz von Message geschrieben.
public void MyLocalEntryMethod()
{
// this methd will create an entry span and then call our method
// that communicates with the remote-service (and thus create our exit span)
using(var span = CustomSpan.CreateEntry())
{
span.WrapAction(()=>{
CallRemoteService();
})
}
}
public void CallRemoteService()
{
Message message = new Message();
using(var exitSpan = CustomSpan.CreateExit(this, message.AddTag))
{
exitSpan.WrapAction( ()=>{
var service = new RemoteService();
service.ValidateRequest(message);
}
}
}
Wenn also unsere Nachricht (Message) den Bereich unserer lokalen Komponente mit einem Aufruf an
service.ValidateRequest(message) verlässt, enthält sie die Korrelationsdaten in ihrer Tagliste. Im
Folgenden ist dargestellt, wie diese Daten auf der Site des Aufrufempfängers extrahiert werden.
public void ValidateRequest(Message message)
{
using(var span = CustomSpan.CreateEntry(this, ()=>ExtractCorrelationData(message))
{
// do whatever this method is supposed to do, we only care for extraction
// if the correlation-data anyway :-)
}
}
private DistributedTraceInformation ExtractCorrelationData(Message message)
{
var dti = new DistributedTraceInformation();
dti.ParentSpanId = Convert.ToInt64(message.Tags[TracingConstants.ExternalParentSpanIdHeader], 16);
dti.TraceId = Convert.ToInt64(message.Tags[TracingConstants.ExternalTraceIdHeader], 16);
return dti;
}
In diesem Fall ist der relevante Teil ExtractCorrelationData. Da bei der Erstellung des Exits die
Methode Message.AddTag verwendet wurde, schreibt das SDK die relevante ID (ParentSpanId und TraceId) in die
Tagliste der Nachricht, bevor es sie an den Service sendet.
Nun kann der Dienst diese Werte wieder abrufen, indem er die Tags ausliest (Sie erkennen sie an den Konstanten, die im vorangegangenen Code-Beispiel als Schlüssel verwendet wurden). Die Instanz von
DistributedTraceInformation, die wir erstellen, wird dann vom SDK verwendet, um den neu erstellten Exit
an den bereits vorhandenen Trace anzuhängen. Die Eingangsspanne, die wir erstellen, wird somit zur Entsprechung des Exits für den
lokalen Service.
Traces aufteilen
Es gibt Beispiele mit zu ausführlichem Tracing, bei denen der resultierende Trace zu lang ist, um verständlich zu sein. Dies kann zum Beispiel bei langer Duplex-WCF-Kommunikation vorkommen, wenn der Server Aktualisierungen über Callback bezüglich des Fortschritts einer Hintergrundtask mit langer Laufzeit per Push-Operation an die Clients überträgt. Diese Art von Kommunikation resultiert in einem langen Trace, der möglicherweise zu lang ist, um in der Benutzerschnittstelle angezeigt zu werden.
In solchen Fällen besteht die Möglichkeit, den Trace in mehrere Traces aufzuteilen, genauer gesagt, jeden Callback an den Client in einen separaten Trace einzuteilen. Dazu können Sie CreateEntryForNewTrace aus CustomSpan verwenden, um den aktuellen Trace zu stoppen und einen neuen Trace ab diesem Punkt zu erstellen. Dies sollten Sie auf der Serverseite tun, kurz vor dem Aufruf an den Client:
var callbackChanell = OperationContext.Current.GetCallbackChannel<IMathResult>();
if (callbackChanell != null)
{
using (var span = CustomSpan.CreateEntryForNewTrace(this))
{
callbackChanell.SendStatusUpdate(new MathArguments() { InParam = args.InParam, Progress = (float)i / args.InParam, Result = generator.Next(1000, 9999) });
}
}
Ist das Vorgehen verständlich?
Inzwischen sollten Sie in der Lage sein, Ihre ersten angepassten Traces mit dem SDK zu erstellen. Wenn Sie Fragen (oder Wünsche!) Wenden Sie sich bezüglich des SDK gerne über den Supportan uns. Viel Erfolg beim Tracing!