.NET Framework SDK de traçage
Le traçage avec Instana est automatique, mais si vous souhaitez bénéficier d'une visibilité encore plus poussée sur du code personnalisé, une zone spécifique de l'application ou un composant interne, vous pouvez utiliser le SDK de traçage .NET d' Instana, comme décrit ci-dessous.
Installation du kit SDK
Nous fournissons le kit SDK pour .NET sous forme de package nuget-package sur le flux officiel de nuget.org. Recherchez Instana.ManagedTracing.Sdk pour le trouver et l'ajouter à votre projet. Après l'installation, votre projet aura deux références supplémentaires (Instana.ManagedTracing.Sdk et Instana.ManagedTracing.Api).
Traçage de votre propre code
Les exemples suivants illustrent comment ajouter des fonctions de traçage distribué à votre code.
Création d'une étendue simple
Le moyen le plus simple de tracer un appel de méthode consiste à utiliser l'API CustomSpan.Create.
public void MyTracedMethod()
{
using(var span = CustomSpan.Create())
{
// your original code here
}
}
Ce code crée un intervalle intermédiaire qui représente votre méthode et le temps passé à son exécution.
Si, au lieu d'utiliser une étendue intermédiaire, vous souhaitez créer une étendue d'entrée (entry span) ou de sortie (exit span), il existe des API pratiques pour cela (appelées CustomSpan.CreateEntry et CustomSpan.CreateExit).
Création d'une étendue simple et capture d'exceptions
Si vous souhaitez annoter les étendues créées avec les erreurs éventuelles qui se sont produites lors du traitement du corps de la méthode, vous pouvez bien sûr le faire manuellement à l'aide de l'API CustomSpan.SetError, comme suit :
public void MyTracedMethod()
{
using(var span = CustomSpan.Create())
{
try
{
// your original code here
}
catch(Exception e)
{
span.SetError(e);
}
}
}
Une approche plus simple et préférée consiste à utiliser les API CustomSpan.WrapAction ou CustomSpan.Wrap<T> à la place.
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);
}
}
Si le bloc de code que vous souhaitez encapsuler renvoie des données dont vous avez besoin pour la suite du traitement, utilisez plutôt la méthode ` 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);
}
}
Nous savons maintenant comment créer des étendues d'entrée et de sortie et des étendues intermédiaires. Nous avons également appris à capturer des exceptions à l'aide des API SetError ou WrapAction / Wrap<T> .
Ajout de données dans vos étendues
Une étendue proprement dite est constituée d'un calcul de temps, d'une pile d'appels et d'un nom. C'est bien, mais pas très utile dans la plupart des cas. Alors, comment ajouter des données dans votre étendue ? Une étendue peut contenir des données (Data) et des balises (Tags), pour lesquelles la classe CustomSpan propose des API simples.
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);
}
}
Les données sont transportées vers le système de back-end et peuvent être récupérées dans l'interface utilisateur en téléchargeant une trace. Cependant, les données indiquées ici n'apparaissent pas dans l'interface utilisateur d' Instana.
Ajout de balises dans vos étendues
Au lieu de SetData, vous pouvez également utiliser SetTag, qui prend un tableau de chaînes comme clé (ce qui peut servir à structurer les données que vous transmettez à l'étendue sous forme de hiérarchie).
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);
}
}
Les balises s'affichent directement dans la vue des détails des appels et peuvent également être recherchées dans le cadre d'une analyse illimitée.
Mappage de vos étendues personnalisées à un service
En général, vous souhaiterez associer vos étendues personnalisées à un service logique dans les perspectives d'application d'Instana. C'est aussi facile que d'appeler l'API SetServicename, qui nécessite juste une chaîne unique. Pour faire la distinction entre les différents noeuds finaux implémentés à l'aide du kit SDK, vous pouvez également indiquer un noeud final pour obtenir un mappage plus détaillé avec l'API SetEndpointName.
public void MyTracedMethod(string userName, string someSuperRelevantData)
{
using(var span = CustomSpan.Create())
{
span.SetServiceName("AwesomeSDKService");
span.SetEndpointName("TracingEndpoint");
.
.
.
}
}
Bien que vous puissiez définir un service et un noeud final sur chaque étendue, il est important de noter que ces paramètres seront supprimés pour une étendue intermédiaire (INTERMEDIATE) (ils seront hérités de la dernière étendue ENTRY).
Capture du résultat de votre étendue
Admettons que la méthode que vous utilisez renvoie une valeur. Supposons que la présence de cette valeur dans votre trace soit utile pour le traitement des incidents. Entrez la méthode d'API SetResult.
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;
}
}
Imbrication d'étendues
Imbriquer les étendues est aussi simple que d'appeler une méthode à partir d'une autre. Pour plus de précision, voici un exemple. Nous supposons qu'une méthode sert d'entrée, tandis que son étendue enfant sert d'intermédiaire.
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;
}
}
Vous obtenez ainsi une étendue d'entrée (entry-span) ayant comme enfant une étendue intermédiaire. Techniquement, l'imbrication n'a pas de limites, mais vous ne devez pas créer d'étendues avec une récurrence trop importante.
Analyse de la traçabilité distributed distribuée
Toutes les étendues que nous avons examinées jusqu'à présent se limitaient à un seul service. Elles n'englobaient aucun autre composant qui aurait également effectué le traçage de leur activité. Pour obtenir une véritable trace distribuée dans les limites du service, vous devez établir des corrélations.
Une corrélation dans le domaine du traçage montre comment définir les données de corrélation sur un appel de sortie, pour ensuite les récupérer et "continuer" ce contexte sur l'entrée de l'autre composant.
Pour y parvenir, le paramètre CustomSpan contient une surcharge de méthodes CustomSpan.CreateExit et CustomSpan.CreateEntry.
Alors que CustomSpan.CreateExit peut consommer un Action<string, string> comme argument, CustomSpan.CreateEntry prend un Func<DistributedTraceInformation>.
Mais comment ça fonctionne ?
Supposons que nous ayons une Message classe que nous transmettons au service distant que nous appelons.
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);
}
}
La partie qui nous intéresse ici est la méthode AddTag, qui comprend deux chaînes. Il s'agit de la signature que nous devons fournir à la méthode CustomSpan.CreateExit.
L'utilisation de cette méthode lors de l'appel de CreateExit va écrire les données de corrélation dans notre instance Message.
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);
}
}
}
Ainsi, lorsque notre message laisse l'étendue de notre composant local avec un appel à service.ValidateRequest(message), il intègre les données de corrélation dans sa liste de balises. Voyons comment ces données seront extraites sur le site du destinataire de l'appel.
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;
}
La partie qui nous intéresse est ExtractCorrelationData. Etant donné que nous avons utilisé la méthode Message.AddTag lors de la création de l'étendue de sortie, le kit SDK écrit l'ID correspondant (ID de l'étendue parent et ID de trace) dans la liste de balises du message avant de l'envoyer au service.
Le service peut désormais récupérer ces valeurs en lisant les balises (vous pouvez les identifier grâce aux constantes utilisées comme clés dans l'exemple de code précédent). L'instance DistributedTraceInformation que nous avons créée sera alors utilisée par le kit SDK pour ajouter la nouvelle étendue de sortie à la trace déjà existante. L'étendue d'entrée que nous avons créée sera donc la contrepartie de l'étendue de sortie sur le service local.
Division des traces
Il existe des exemples où le traçage automatisé en fait trop, et où la trace créée est trop longue pour être comprise. Par exemple, cela peut se produire dans une communication WCF en duplex long, lorsque le serveur envoie des mises à jour aux clients par le biais d'un rappel sur la progression d'une longue tâche d'arrière-plan. Ce type de communication donne lieu à une longue trace qui peut être potentiellement trop longue pour être affichée dans l'interface utilisateur.
Dans ce cas, il est possible de diviser la trace en plusieurs traces, plus précisément, de diviser chaque rappel au client en une trace distincte. Pour ce faire, vous pouvez utiliser CreateEntryForNewTrace à partir de CustomSpan pour arrêter la trace actuelle et en créer une nouvelle à partir de ce point. Faire-le côté serveur, juste avant l'appel au 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) });
}
}
Vous avez compris ?
Vous devez désormais être en mesure de créer vos premières traces personnalisées à l'aide de notre kit SDK. Si vous avez des questions (ou des suggestions) en ce qui concerne le SDK, n'hésitez pas à nous contacter via le support. Bonne continuation avec le traçage !