Propagation du contexte de traces
La propagation du contexte de trace est le mécanisme permettant de transmettre des informations de traçage telles que l’ID de trace, l’ID de span et les décisions d’échantillonnage d’une partie d’une application distribuée à une autre. Cela permet de corréler toutes les traces (et la télémétrie supplémentaire) dans une requête. Lorsque l’instrumentation automatique est activée, la propagation du contexte de trace est gérée automatiquement par le SDK Datadog.
Par défaut, le SDK Datadog extrait et injecte les en-têtes de traçage distribué en utilisant les formats suivants :
Cette configuration par défaut maximise la compatibilité avec les anciennes versions du SDK Datadog et les produits tout en permettant l’interopérabilité avec d’autres systèmes de traçage distribué comme OpenTelemetry.
Personnaliser la propagation du contexte de trace
Vous devrez peut-être personnaliser la configuration de la propagation du contexte de trace si vos applications :
- Communiquent des informations de traçage distribué dans un format pris en charge différent
- Doivent empêcher l’extraction ou l’injection des en-têtes de traçage distribué
Utilisez les variables d’environnement suivantes pour configurer les formats de lecture et d’écriture des en-têtes de traçage distribué. Référez-vous à la section Support des langages pour les valeurs de configuration spécifiques aux langages.
DD_TRACE_PROPAGATION_STYLE- Spécifie les formats de propagation du contexte de trace pour l’extraction et l’injection dans une liste séparée par des virgules. Peut être remplacé par des configurations spécifiques à l’extraction ou à l’injection.
Par défaut: datadog,tracecontext,baggage
Remarque: Avec plusieurs formats de contexte de trace, l’extraction suit l’ordre spécifié (par exemple, datadog,tracecontext vérifie d’abord les en-têtes Datadog). Le premier contexte valide continue la trace ; les contextes valides supplémentaires deviennent des liens de span. Lorsque baggage est inclus, il est ajouté en tant que baggage au contexte existant. OTEL_PROPAGATORS- Spécifie les formats de propagation du contexte de trace pour l’extraction et l’injection (liste séparée par des virgules). La priorité la plus basse ; elle est ignorée si une autre variable d’environnement de propagation du contexte de trace Datadog est définie.
Remarque: N’utilisez cette configuration que lors de la migration d’une application du SDK OpenTelemetry vers le SDK Datadog. Pour plus d’informations sur cette configuration et d’autres variables d’environnement OpenTelemetry, voir Utilisation des variables d’environnement OpenTelemetry avec les SDK Datadog. DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT- Spécifie comment les en-têtes de traçage distribué entrants doivent être gérés au niveau du service. Les valeurs acceptées sont :
continue : Le SDK continuera la trace distribuée si les en-têtes de traçage distribués entrants représentent un contexte de trace valide.
restart : Le SDK commencera toujours une nouvelle trace. Si les en-têtes de traçage distribués entrants représentent un contexte de trace valide, ce contexte de trace sera représenté comme un lien de span sur les spans d’entrée de service (par opposition au span parent dans la configuration continue).
ignore : Le SDK commencera toujours une nouvelle trace et tous les en-têtes de traçage distribués entrants sont ignorés.
Par défaut : continue
Remarque : Ceci n’est implémenté que dans les bibliothèques .NET, Node.js, Python et Java.
Configuration avancée
La plupart des services envoient et reçoivent des en-têtes de contexte de trace en utilisant le même format. Cependant, si votre service doit accepter des en-têtes de contexte de trace dans un format et les envoyer dans un autre, utilisez ces configurations :
DD_TRACE_PROPAGATION_STYLE_EXTRACT- Spécifie les formats de propagation du contexte de trace pour l’extraction uniquement dans une liste séparée par des virgules. La priorité la plus élevée pour configurer les propagateurs d’extraction.
DD_TRACE_PROPAGATION_STYLE_INJECT- Spécifie les formats de propagation du contexte de trace pour l’injection uniquement dans une liste séparée par des virgules. La plus haute priorité pour configurer les propagateurs d’injection.
Le SDK Datadog prend en charge les formats de contexte de trace suivants :
* Remarque : baggage n’est pas pris en charge en Rust.
Support des langues
Le SDK Java Datadog prend en charge les formats de contexte de trace suivants, y compris les valeurs de configuration obsolètes :
Configuration supplémentaire
En plus de la configuration des variables d’environnement, vous pouvez également mettre à jour les propagateurs en utilisant la configuration des propriétés système :
-Ddd.trace.propagation.style=datadog,b3multi-Dotel.propagators=datadog,b3multi-Ddd.trace.propagation.style.inject=datadog,b3multi-Ddd.trace.propagation.style.extract=datadog,b3multi
Le SDK Python de Datadog prend en charge les formats de contexte de trace suivants, y compris les valeurs de configuration obsolètes :
Le SDK Ruby de Datadog prend en charge les formats de contexte de trace suivants, y compris les valeurs de configuration obsolètes :
Configuration supplémentaire
En plus de la configuration des variables d’environnement, vous pouvez également mettre à jour les propagateurs dans le code en utilisant Datadog.configure :
Datadog.configure do |c|
# List of header formats that should be extracted
c.tracing.propagation_extract_style = [ 'tracecontext', 'datadog', 'b3' ]
# List of header formats that should be injected
c.tracing.propagation_inject_style = [ 'tracecontext', 'datadog' ]
end
Le SDK Go de Datadog prend en charge les formats de contexte de trace suivants, y compris les valeurs de configuration obsolètes :
Le SDK Datadog Node.js prend en charge les formats de contexte de trace suivants, y compris les valeurs de configuration obsolètes :
Le SDK Datadog PHP prend en charge les formats de contexte de trace suivants, y compris les valeurs de configuration obsolètes :
Cas d’utilisation supplémentaires
Les cas d’utilisation suivants sont spécifiques au SDK Datadog PHP :
Lorsqu’un nouveau script PHP est lancé, le SDK Datadog vérifie automatiquement la présence des en-têtes Datadog pour le traçage distribué :
x-datadog-trace-id (variable d’environnement : HTTP_X_DATADOG_TRACE_ID)x-datadog-parent-id (variable d’environnement : HTTP_X_DATADOG_PARENT_ID)x-datadog-origin (variable d’environnement : HTTP_X_DATADOG_ORIGIN)x-datadog-tags (variable d’environnement : HTTP_X_DATADOG_TAGS)
Pour définir manuellement les informations de traçage dans un script CLI pour de nouvelles traces ou des traces existantes, utilisez la fonction DDTrace\set_distributed_tracing_context(string $trace_id, string $parent_id, ?string $origin = null, ?array $tags = null).
<?php
function processIncomingQueueMessage($message) {
}
\DDTrace\trace_function(
'processIncomingQueueMessage',
function(\DDTrace\SpanData $span, $args) {
$message = $args[0];
\DDTrace\set_distributed_tracing_context($message->trace_id, $message->parent_id);
}
);
Pour la version 0.87.0 et ultérieure, si les en-têtes bruts sont disponibles, utilisez la fonction DDTrace\consume_distributed_tracing_headers(array|callable $headersOrCallback). Remarque : Les noms des en-têtes doivent être en minuscules.
$headers = [
"x-datadog-trace-id" => "1234567890",
"x-datadog-parent-id" => "987654321",
];
\DDTrace\consume_distributed_tracing_headers($headers);
Pour extraire le contexte de trace directement sous forme d’en-têtes, utilisez la fonction DDTrace\generate_distributed_tracing_headers(?array $inject = null): array.
$headers = DDTrace\generate_distributed_tracing_headers();
// Store headers somewhere, inject them in an outbound request, ...
// These $headers can also be read back by \DDTrace\consume_distributed_tracing_headers from another process.
L’argument optionnel de cette fonction accepte un tableau de noms de styles d’injection. La valeur par défaut est le style d’injection configuré.
Le SDK PHP prend en charge le traçage automatique de la bibliothèque php-amqplib/php-amqplib (version 0.87.0+). Cependant, dans certains cas, votre trace distribuée peut être déconnectée. Par exemple, lors de la lecture de messages à partir d’une file d’attente distribuée en utilisant la méthode basic_get en dehors d’une trace existante, vous devez ajouter une trace personnalisée autour de l’appel basic_get et du traitement des messages correspondant :
// Create a surrounding trace
$newTrace = \DDTrace\start_trace_span();
$newTrace->name = 'basic_get.process';
$newTrace->service = 'amqp';
// basic_get call(s) + message(s) processing
$msg = $channel->basic_get($queue);
if ($msg) {
$messageProcessing($msg);
}
// Once done, close the span
\DDTrace\close_span();
Créer cette trace environnante pour votre logique de consommation et de traitement garantit l’observabilité de votre file d’attente distribuée.
Le SDK C++ de Datadog prend en charge les formats de contexte de trace suivants, y compris les valeurs de configuration obsolètes :
Configuration supplémentaire
En plus de la configuration des variables d’environnement, vous pouvez également mettre à jour les propagateurs dans le code :
#include <datadog/tracer_config.h>
#include <datadog/propagation_style.h>
namespace dd = datadog::tracing;
int main() {
dd::TracerConfig config;
config.service = "my-service";
// `injection_styles` indicates with which tracing systems trace propagation
// will be compatible when injecting (sending) trace context.
// All styles indicated by `injection_styles` are used for injection.
// `injection_styles` is overridden by the `DD_TRACE_PROPAGATION_STYLE_INJECT`
// and `DD_TRACE_PROPAGATION_STYLE` environment variables.
config.injection_styles = {dd::PropagationStyle::DATADOG, dd::PropagationStyle::B3};
// `extraction_styles` indicates with which tracing systems trace propagation
// will be compatible when extracting (receiving) trace context.
// Extraction styles are applied in the order in which they appear in
// `extraction_styles`. The first style that produces trace context or
// produces an error determines the result of extraction.
// `extraction_styles` is overridden by the
// `DD_TRACE_PROPAGATION_STYLE_EXTRACT` and `DD_TRACE_PROPAGATION_STYLE`
// environment variables.
config.extraction_styles = {dd::PropagationStyle::W3C};
...
}
Cas d’utilisation supplémentaires
Pour extraire le contexte de propagation, implémentez une interface DictReader personnalisée et appelez Tracer::extract_span ou Tracer::extract_or_create_span.
Voici un exemple d’extraction du contexte de propagation à partir des en-têtes HTTP :
#include <datadog/dict_reader.h>
#include <datadog/optional.h>
#include <datadog/string_view.h>
#include <unordered_map>
namespace dd = datadog::tracing;
class HTTPHeadersReader : public datadog::tracing::DictReader {
std::unordered_map<dd::StringView, dd::StringView> headers_;
public:
HTTPHeadersReader(std::unordered_map<dd::StringView, dd::StringView> headers)
: headers_(std::move(headers)) {}
~HTTPHeadersReader() override = default;
// Return the value at the specified `key`, or return `nullopt` if there
// is no value at `key`.
dd::Optional<dd::StringView> lookup(dd::StringView key) const override {
auto found = headers_.find(key);
if (found == headers_.cend()) return dd::nullopt;
return found->second;
}
// Invoke the specified `visitor` once for each key/value pair in this object.
void visit(
const std::function<void(dd::StringView key, dd::StringView value)>& visitor)
const override {
for (const auto& [key, value] : headers_) {
visitor(key, value);
}
};
};
// Usage example:
void handle_http_request(const Request& request, datadog::tracing::Tracer& tracer) {
HTTPHeadersReader reader{request.headers};
auto maybe_span = tracer.extract_span(reader);
..
}
Pour injecter le contexte de propagation, implémentez l’interface DictWriter et appelez Span::inject sur une instance de span :
#include <datadog/dict_writer.h>
#include <datadog/string_view.h>
#include <string>
#include <unordered_map>
using namespace dd = datadog::tracing;
class HTTPHeaderWriter : public dd::DictWriter {
std::unordered_map<std::string, std::string>& headers_;
public:
explicit HTTPHeaderWriter(std::unordered_map<std::string, std::string>& headers) : headers_(headers) {}
~HTTPHeaderWriter() override = default;
void set(dd::StringView key, dd::StringView value) override {
headers_.emplace(key, value);
}
};
// Usage example:
void handle_http_request(const Request& request, dd::Tracer& tracer) {
auto span = tracer.create_span();
HTTPHeaderWriter writer(request.headers);
span.inject(writer);
// `request.headers` now populated with the headers needed to propagate the span.
..
}
Le SDK .NET de Datadog prend en charge les formats de contexte de trace suivants, y compris les valeurs de configuration obsolètes :
Cas d’utilisation supplémentaires
- À partir de la version [2.48.0][6], le style de propagation par défaut est
datadog, tracecontext. Cela signifie que les en-têtes Datadog sont utilisés en premier, suivis par le contexte de trace W3C. - Avant la version 2.48.0, l’ordre était
tracecontext, Datadog pour la propagation d’extraction et d’injection. - Avant la version [2.22.0][7], seul le style d’injection
Datadog était activé. - À partir de la version [2.42.0][8], lorsque plusieurs extracteurs sont spécifiés, la configuration
DD_TRACE_PROPAGATION_EXTRACT_FIRST=true indique si l’extraction de contexte doit se terminer immédiatement après la détection du premier tracecontext valide. La valeur par défaut est false.
Dans la plupart des cas, l’extraction et l’injection des en-têtes sont automatiques. Cependant, il existe certains cas connus où votre trace distribuée peut être déconnectée. Par exemple, lors de la lecture de messages à partir d’une file d’attente distribuée, certaines bibliothèques peuvent perdre le contexte de span. Cela se produit également si vous définissez DD_TRACE_KAFKA_CREATE_CONSUMER_SCOPE_ENABLED sur false lors de la consommation de messages Kafka. Dans ces cas, vous pouvez ajouter une trace personnalisée en utilisant le code suivant :
var spanContextExtractor = new SpanContextExtractor();
var parentContext = spanContextExtractor.Extract(headers, (headers, key) => GetHeaderValues(headers, key));
var spanCreationSettings = new SpanCreationSettings() { Parent = parentContext };
using var scope = Tracer.Instance.StartActive("operation", spanCreationSettings);
Fournissez la méthode GetHeaderValues. La manière dont cette méthode est implémentée dépend de la structure qui transporte SpanContext.
Voici quelques exemples :
// Confluent.Kafka
IEnumerable<string> GetHeaderValues(Headers headers, string name)
{
if (headers.TryGetLastBytes(name, out var bytes))
{
try
{
return new[] { Encoding.UTF8.GetString(bytes) };
}
catch (Exception)
{
// ignored
}
}
return Enumerable.Empty<string>();
}
// RabbitMQ
IEnumerable<string> GetHeaderValues(IDictionary<string, object> headers, string name)
{
if (headers.TryGetValue(name, out object value) && value is byte[] bytes)
{
return new[] { Encoding.UTF8.GetString(bytes) };
}
return Enumerable.Empty<string>();
}
// SQS
public static IEnumerable<string> GetHeaderValues(IDictionary<string, MessageAttributeValue> headers, string name)
{
// For SQS, there are a maximum of 10 message attribute headers,
// so the Datadog headers are combined into one header with the following properties:
// - Key: "_datadog"
// - Value: MessageAttributeValue object
// - DataType: "String"
// - StringValue: <JSON map with key-value headers>
if (headers.TryGetValue("_datadog", out var messageAttributeValue)
&& messageAttributeValue.StringValue is string jsonString)
{
var datadogDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString);
if (datadogDictionary.TryGetValue(name, out string value))
{
return new[] { value };
}
}
return Enumerable.Empty<string>();
}
Lors de l’utilisation de l’API SpanContextExtractor pour tracer les spans de consommateur Kafka, définissez DD_TRACE_KAFKA_CREATE_CONSUMER_SCOPE_ENABLED sur false. Cela garantit que le span du consommateur est correctement fermé immédiatement après que le message est consommé du sujet, et que les métadonnées (telles que partition et offset) sont enregistrées correctement. Les spans créés à partir des messages Kafka via l’API SpanContextExtractor sont les enfants du span du producteur et des spans au même niveau que le span du consommateur.
Si vous devez propager le contexte de trace manuellement (pour les bibliothèques qui ne sont pas instrumentées automatiquement, comme le client WCF), vous pouvez utiliser l’API SpanContextInjection. Voici un exemple pour WCF où this est le client WCF :
using (OperationContextScope ocs = new OperationContextScope(this.InnerChannel))
{
var spanContextInjector = new SpanContextInjector();
spanContextInjector.Inject(OperationContext.Current.OutgoingMessageHeaders, SetHeaderValues, Tracer.Instance.ActiveScope?.Span?.Context);
}
void SetHeaderValues(MessageHeaders headers, string name, string value)
{
MessageHeader header = MessageHeader.CreateHeader(name, "datadog", value);
headers.Add(header);
}
Le SDK Rust de Datadog est en préversion.
Le SDK Rust de Datadog est construit sur le SDK OpenTelemetry (OTel).
La propagation du contexte de trace est gérée par le SDK OTel, qui est configuré par datadog-opentelemetry pour prendre en charge les formats datadog et tracecontext (W3C).
Configuration
Vous pouvez contrôler quels formats de propagation sont utilisés en définissant la variable d’environnement DD_TRACE_PROPAGATION_STYLE. Vous pouvez fournir une liste séparée par des virgules.
Exemple :
# To support both W3C and Datadog
export DD_TRACE_PROPAGATION_STYLE="tracecontext,datadog"
Injection et extraction manuelles
Parce qu’il n’y a pas d’instrumentation automatique pour Rust, vous devez propager manuellement le contexte lors de l’exécution ou de la réception d’appels distants (comme les requêtes HTTP).
HeaderExtractor pour extraire un contexte parent des en-têtes de requête entrants.HeaderInjector pour injecter le contexte actuel dans les en-têtes de requête sortants.
Tout d’abord, ajoutez opentelemetry-http à votre Cargo.toml.
[dependencies]
# Provides HeaderInjector and HeaderExtractor
# Ensure this version matches your other opentelemetry dependencies
opentelemetry-http = "0.31"
# Only required for the Hyper examples below
http-body-util = "0.1"
Utilisez la même version de crate pour opentelemetry-http que le reste de vos dépendances OpenTelemetry afin d'éviter les conflits de version.
Injection de contexte (côté client)
Lors de l’envoi d’une requête HTTP (par exemple, avec hyper 1.0), injectez le contexte de la portée actuelle dans les en-têtes de la requête en utilisant HeaderInjector.
use opentelemetry::{global, Context};
use opentelemetry_http::HeaderInjector;
use hyper::Request;
use http_body_util::Empty;
use hyper::body::Bytes;
// HYPER example
fn build_outbound_request(url: &str) -> http::Result<Request<Empty<Bytes>>> {
let cx = Context::current();
// Build the request and inject headers in-place
let mut builder = Request::builder().method("GET").uri(url);
global::get_text_map_propagator(|prop| {
prop.inject_context(&cx, &mut HeaderInjector(builder.headers_mut().unwrap()))
});
builder.body(Empty::<Bytes>::new())
}
Lors de la réception d’une requête HTTP, extrayez le contexte de trace des en-têtes en utilisant HeaderExtractor.
Lorsque vous utilisez des environnements d’exécution asynchrones (comme Tokio), vous devez attacher le contexte extrait à l’avenir afin qu’il se propage correctement à travers la chaîne de tâches asynchrones.
use opentelemetry::{
global,
trace::{Span, FutureExt, SpanKind, Tracer},
Context,
};
use opentelemetry_http::HeaderExtractor;
use hyper::{Request, Response};
use hyper::body::Incoming;
use http_body_util::Full;
use hyper::body::Bytes;
// Utility function to extract context from a hyper request
fn extract_context(req: &Request<Incoming>) -> Context {
global::get_text_map_propagator(|propagator| {
propagator.extract(&HeaderExtractor(req.headers()))
})
}
// A placeholder for your actual request handling logic
async fn your_handler_logic() -> Response<Full<Bytes>> {
// ... your logic ...
Response::new(Full::new(Bytes::from("Hello, World!")))
}
// HYPER example
async fn hyper_handler(req: Request<Incoming>) -> Response<Full<Bytes>> {
// Extract the parent context from the incoming headers
let parent_cx = extract_context(&req);
let tracer = global::tracer("my-server-component");
// Start the server span as a child of the extracted context
let server_span = tracer
.span_builder("http.server.request")
.with_kind(SpanKind::Server)
.start_with_context(tracer, &parent_cx);
// Create a new context with the new server span
// This is critical for async propagation
let cx = parent_cx.with_span(server_span);
// Attach the new context to the future using .with_context(cx)
// This makes the span active for the duration of the handler
your_handler_logic().with_context(cx).await
}
Lorsque le SDK Datadog est configuré avec le format Datadog pour l’extraction ou l’injection (possiblement les deux), le SDK Datadog interagit avec les en-têtes de requête suivants :
x-datadog-trace-id- Spécifie les 64 bits inférieurs de l’identifiant de trace 128 bits, au format décimal.
x-datadog-parent-id- Spécifie l’identifiant de portée de 64 bits de la portée actuelle, au format décimal.
x-datadog-origin- Spécifie le produit Datadog qui a initié la trace, tel que Real User Monitoring ou Synthetic Monitoring. Si cet en-tête est présent, la valeur doit être l’un de:
rum, synthetics, synthetics-browser. x-datadog-sampling-priority- Spécifie la décision d’échantillonnage prise pour la portée représentée sous forme d’entier, au format décimal.
x-datadog-tags- Spécifie des informations d’état de trace Datadog supplémentaires, y compris, mais sans s’y limiter, les 64 bits supérieurs de l’identifiant de trace 128 bits (au format hexadécimal).
Lorsque le SDK Datadog est configuré avec le format None pour l’extraction ou l’injection (possiblement les deux), le SDK Datadog ne n’interagit pas avec les en-têtes de requête, ce qui signifie que l’opération de propagation de contexte correspondante ne fait rien.
Baggage
Par défaut, Baggage est automatiquement propagé à travers une requête distribuée en utilisant les en-têtes compatibles W3C d’OpenTelemetry. Pour désactiver le baggage, définissez DD_TRACE_PROPAGATION_STYLE sur datadog,tracecontext.
Par défaut, user.id,session.id,account.id les clés de baggage sont ajoutées en tant que balises span. Pour personnaliser cette configuration, voir Configuration de la propagation du contexte. Les clés de baggage spécifiées sont automatiquement ajoutées en tant que balises span baggage.<key> (par exemple, baggage.user.id).
La prise en charge du baggage en tant que balises span a été introduite dans les versions suivantes :
| Langue | Version minimale du SDK |
|---|
| Java | 1.52.0 |
| Python | 3.7.0 |
| Ruby | 2.20.0 |
| Go | 2.2.2 |
| .NET | 3.23.0 |
| Node | 5.54.0 |
| PHP | 1.10.0 |
| C++/Proxy | 1.9.0 (Nginx). Les autres proxies ne sont pas pris en charge. |
| Rust | Non pris en charge |
Lectures complémentaires
Documentation, liens et articles supplémentaires utiles: