How to setup HTTP Requests Tracing using Opentelemetry SDK
Last updated: June 12, 2026
Introduction
This guide explains how to instrument your application's HTTP API requests using the OpenTelemetry SDK and send that trace data to Astra for API discovery and security testing.
When an HTTP request is processed in your application, the OpenTelemetry SDK captures key details about it — the method, URL, headers, body, status code, and network information — and exports them as spans to Astra Traffic Collector. This gives Astra the visibility it needs to build and maintain an accurate API inventory.
The SDK handles tracing asynchronously, so instrumentation does not block your request flow or add latency to your API handlers. If the collector is temporarily unavailable, spans are dropped rather than queued indefinitely — ensuring your application is never impacted.
Who Should Read This
This guide is written for application developers responsible for adding observability to their services. You will be working directly inside REST controllers or HTTP request handlers, where both the incoming request and outgoing response are accessible.
This guide is relevant to you if you are:
Integrating the OpenTelemetry SDK into a new or existing service
Setting up HTTP tracing for API inventory discovery in Astra
Working in Go, Java, C#, Node.js, or Python
Prerequisites
Astra Traffic Collector is installed and accessible
Opentelemetry SDK integration should be created and keep the
sensorIDhandy
Runtime Characteristics
The OpenTelemetry SDK is asynchronous in nature:
When attributes are added to spans, they are buffered in memory.
Exporting to the configured backend (Astra Traffic Collector) happens asynchronously.
This means instrumentation does not block the main request flow and does not add significant latency to API handling.
Developers should note:
The actual cost of creating spans is lightweight and non-blocking.
Exporting traces depends on the chosen exporter, but the SDK ensures that spans are sent out of band.
If the exporter backend is unavailable, spans are dropped rather than blocking the API handler.
Tracing Selection Criteria
Only successful HTTP requests should be selected for tracing. This ensures that trace data reflects valid service interactions, rather than clutter from failed requests.
Include:
Status codes in the 2xx range (e.g.,
200,201)Status codes in the 3xx range (e.g.,
301,302)
Exclude:
Status codes in the 4xx range (client errors)
Status codes in the 5xx range (server errors)
Resource Attributes
Resource attributes describe the service or application emitting telemetry data. These attributes are set once at the resource level (not per span).
Attribute Name | Type | Description | Example |
| string | Version of the deployed sensor agent |
|
Required Span Attributes
Each span must include a consistent set of attributes. These attributes provide details about the request, network, headers, and bodies.
1. HTTP Request Metadata
Attribute Name | Type | Description | Example |
| string | integration ID aka |
|
| string | HTTP method used |
|
| string | Request scheme |
|
| string | HTTP protocol version |
|
| string | Hostname or domain |
|
| string | URL path including query string |
|
| int | Response HTTP status code |
|
2. Network Information
Attribute Name | Type | Description | Example |
| string | Client IP address or unique identifier |
|
3. Headers and Body
Attribute Name | Type | Description | Example Value |
| string | JSON string of request headers |
|
| string | JSON string of response headers |
|
| string | Raw request body |
|
| string | Raw response body |
|
Example: Tracing For A Sample HTTP Request
Let's consider an example cURL request and how that request is mapped to otel span attributes for easy understanding
HTTP Request cURL:
curl -X POST 'https://api.example.com/users?source=mobile' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' \
-H 'User-Agent: MyApp/1.0' \
--data-raw '{
"name": "John Doe",
"email": "john.doe@example.com",
"age": 30,
"preferences": {
"theme": "dark",
"notifications": true
}
}'
#Following cURL response is assumed with 200OK status
{
"id": 12345,
"name": "John Doe",
"email": "john.doe@example.com",
"created_at": "2023-12-01T10:30:00Z",
"status": "active"
}Corresponding Span Attributes
import (
"encoding/json"
"log"
)
reqBody := `<root>
<name>Example</name>
<mail>val@yopmail.com</mail>
<age>30</age>
<addresses>
<address city="Anytown1">
<street>123 Main St</street>
</address>
<address city="Anytown2">
<street>124 Main St</street>
</address>
</addresses>
</root>`
respBody := "{\"email\":\"val@yopmail.com\",\"arr\":[1,2,3,4]}"
// Example request headers map
requestHeaders := map[string]string{
"host": "localhost:8080",
"content-type": "application/xml",
"accept": "application/json",
"user-agent": "curl/7.68.0",
"accept-encoding": "gzip, deflate, br",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
}
// Example response headers map
responseHeaders := map[string]string{
"content-type": "application/json",
"content-length": "152",
"x-request-id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"strict-transport-security": "max-age=31536000; includeSubDomains",
"cache-control": "no-cache, no-store, must-revalidate",
}
// Convert request headers to JSON string
requestHeadersJSON, err := json.Marshal(requestHeaders)
if err != nil {
log.Fatalf("Failed to marshal request headers: %v", err)
}
// Convert response headers to JSON string
responseHeadersJSON, err := json.Marshal(responseHeaders)
if err != nil {
log.Fatalf("Failed to marshal response headers: %v", err)
}
// Set span attributes
span.SetAttributes(
attribute.String("sensor.id", "17062dc8-6cd6-4951-9b21-5f40c85b0e71"), // Unique identifier for the sensor
attribute.String("http.method", "GET"), //http methods like: GET/POST/PUT/PATCH
attribute.String("http.scheme", "https"), //url scheme like: http/https
attribute.String("http.flavor", "2.0"), //scheme flavor: 1.0/1.1/2.0
attribute.String("http.host", "localhost"), //host or main domain name: api.mycompany.com
attribute.String("http.target", "/stage-test-client1?param1=10"), //url target. Everything after domain name in this fashion: https://api.mydomain.com/<this_is_url_target_including_prefix_/>
attribute.String("net.sock.peer.addr", "127.0.0.1"), //IP address of incoming request. Or else, any unique identifier of client
attribute.Int("http.status_code", 200), //http status code like: 200/404/500/etc
attribute.String("http.request.headers", string(requestHeadersJSON)), //jsonified string of request header/value map
attribute.String("http.response.headers", string(responseHeadersJSON)), //jsonified string of response header/value map
attribute.String("http.request.body", reqBody), //stringified request body
attribute.String("http.response.body", respBody),//stringified response body
)//Requires Java 8+
String reqBody = "<root>\n" +
" <name>Example</name>\n" +
" <mail>val@yopmail.com</mail>\n" +
" <age>30</age>\n" +
" <addresses>\n" +
" <address city=\"Anytown1\">\n" +
" <street>123 Main St</street>\n" +
" </address>\n" +
" <address city=\"Anytown2\">\n" +
" <street>124 Main St</street>\n" +
" </address>\n" +
" </addresses>\n" +
"</root>";
String respBody = "{\"email\":\"val@yopmail.com\",\"arr\":[1,2,3,4]}";
// Request and response headers
Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("host", "localhost");
requestHeaders.put("content-type", "application/xml");
requestHeaders.put("accept", "application/json");
requestHeaders.put("authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...");
Map<String, String> responseHeaders = new HashMap<>();
responseHeaders.put("content-type", "application/json");
responseHeaders.put("content-length", "152");
responseHeaders.put("x-request-id", "a1b2c3d4-e5f6-7890-abcd-ef1234567890");
// Convert headers to JSON strings
ObjectMapper objectMapper = new ObjectMapper();
String requestHeadersJson = objectMapper.writeValueAsString(requestHeaders);
String responseHeadersJson = objectMapper.writeValueAsString(responseHeaders);
// Set span attributes
Span span = tracer.spanBuilder("http-request").startSpan();
try (Scope scope = span.makeCurrent()) {
// HTTP Request Metadata
span.setAttribute("sensor.id", "17062dc8-6cd6-4951-9b21-5f40c85b0e71"); // Unique identifier for the sensor
span.setAttribute("http.method", "GET"); // HTTP methods like: GET/POST/PUT/PATCH
span.setAttribute("http.scheme", "https"); // URL scheme like: http/https
span.setAttribute("http.flavor", "2.0"); // Scheme flavor: 1.0/1.1/2.0
span.setAttribute("http.host", "localhost"); // Host or main domain name: api.mycompany.com
span.setAttribute("http.target", "/stage-test-client1?param1=10"); // URL target (path + query)
span.setAttribute("net.sock.peer.addr", "127.0.0.1"); // IP address of incoming request
span.setAttribute("http.status_code", 200); // HTTP status code: 200/404/500/etc
// Headers (as JSON strings)
span.setAttribute("http.request.headers", requestHeadersJson);
span.setAttribute("http.response.headers", responseHeadersJson);
// Request and response bodies
span.setAttribute("http.request.body", reqBody);
span.setAttribute("http.response.body", respBody);
// Perform your actual HTTP request logic here
} finally {
span.end();
}string reqBody = @"<root>
<name>Example</name>
<mail>val@yopmail.com</mail>
<age>30</age>
<addresses>
<address city=""Anytown1"">
<street>123 Main St</street>
</address>
<address city=""Anytown2"">
<street>124 Main St</street>
</address>
</addresses>
</root>";
string respBody = "{\"email\":\"val@yopmail.com\",\"arr\":[1,2,3,4]}";
// Request and response headers
var requestHeaders = new Dictionary<string, string>
{
{ "host", "localhost" },
{ "content-type", "application/xml" },
{ "accept", "application/json" },
{ "authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }
};
var responseHeaders = new Dictionary<string, string>
{
{ "content-type", "application/json" },
{ "content-length", "152" },
{ "x-request-id", "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
};
// Convert headers to JSON strings
string requestHeadersJson = JsonSerializer.Serialize(requestHeaders);
string responseHeadersJson = JsonSerializer.Serialize(responseHeaders);
using var activity = MyActivitySource.StartActivity("http-request");
if (activity != null)
{
// HTTP Request Metadata
activity.SetTag("sensor.id", "17062dc8-6cd6-4951-9b21-5f40c85b0e71"); // Unique identifier for the sensor
activity.SetTag("http.method", "GET"); // HTTP methods like: GET/POST/PUT/PATCH
activity.SetTag("http.scheme", "https"); // URL scheme like: http/https
activity.SetTag("http.flavor", "2.0"); // Scheme flavor: 1.0/1.1/2.0
activity.SetTag("http.host", "localhost"); // Host or main domain name: api.mycompany.com
activity.SetTag("http.target", "/stage-test-client1?param1=10"); // URL target (path + query)
activity.SetTag("net.sock.peer.addr", "127.0.0.1"); // IP address of incoming request
activity.SetTag("http.status_code", 200); // HTTP status code: 200/404/500/etc
// Headers (as JSON strings)
activity.SetTag("http.request.headers", requestHeadersJson);
activity.SetTag("http.response.headers", responseHeadersJson);
// Request and response bodies
activity.SetTag("http.request.body", reqBody);
activity.SetTag("http.response.body", respBody);
// Perform your actual HTTP request logic here
}const { trace } = require('@opentelemetry/api');
const { URL } = require('url');
// Get tracer
const tracer = trace.getTracer('com.example.YourApplicationName');
const reqBody = `<root>
<name>Example</name>
<mail>val@yopmail.com</mail>
<age>30</age>
<addresses>
<address city="Anytown1">
<street>123 Main St</street>
</address>
<address city="Anytown2">
<street>124 Main St</street>
</address>
</addresses>
</root>`;
const respBody = '{"email":"val@yopmail.com","arr":[1,2,3,4]}';
// Request and response headers
const requestHeaders = {
"host": "localhost",
"content-type": "application/xml",
"accept": "application/json",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
};
const responseHeaders = {
"content-type": "application/json",
"content-length": "152",
"x-request-id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
};
// Convert headers to JSON strings
const requestHeadersJson = JSON.stringify(requestHeaders);
const responseHeadersJson = JSON.stringify(responseHeaders);
// Create and configure span
const span = tracer.startSpan('http-request');
try {
// HTTP Request Metadata
span.setAttributes({
'sensor.id': '17062dc8-6cd6-4951-9b21-5f40c85b0e71', // Unique identifier for the sensor
'http.method': 'GET', // HTTP methods like: GET/POST/PUT/PATCH
'http.scheme': 'https', // URL scheme like: http/https
'http.flavor': '2.0', // Scheme flavor: 1.0/1.1/2.0
'http.host': 'localhost', // Host or main domain name: api.mycompany.com
'http.target': '/stage-test-client1?param1=10', // URL target (path + query)
'net.sock.peer.addr': '127.0.0.1', // IP address of incoming request
'http.status_code': 200, // HTTP status code: 200/404/500/etc
// Headers (as JSON strings)
'http.request.headers': requestHeadersJson,
'http.response.headers': responseHeadersJson,
// Request and response bodies
'http.request.body': reqBody,
'http.response.body': respBody
});
// Perform your actual HTTP request logic here
// ...
} finally {
span.end();
}import json
from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode
# Get tracer
tracer = trace.get_tracer(__name__)
req_body = """<root>
<name>Example</name>
<mail>val@yopmail.com</mail>
<age>30</age>
<addresses>
<address city="Anytown1">
<street>123 Main St</street>
</address>
<address city="Anytown2">
<street>124 Main St</street>
</address>
</addresses>
</root>"""
resp_body = '{"email":"val@yopmail.com","arr":[1,2,3,4]}'
# Request and response headers
request_headers = {
"host": "localhost",
"content-type": "application/xml",
"accept": "application/json",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
response_headers = {
"content-type": "application/json",
"content-length": "152",
"x-request-id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
# Convert headers to JSON strings
request_headers_json = json.dumps(request_headers, separators=(',', ':'))
response_headers_json = json.dumps(response_headers, separators=(',', ':'))
# Create and configure span
with tracer.start_as_current_span("http-request") as span:
# HTTP Request Metadata
span.set_attribute("sensor.id", "17062dc8-6cd6-4951-9b21-5f40c85b0e71") # Unique identifier for the sensor
span.set_attribute("http.method", "GET") # HTTP methods like: GET/POST/PUT/PATCH
span.set_attribute("http.scheme", "https") # URL scheme like: http/https
span.set_attribute("http.flavor", "2.0") # Scheme flavor: 1.0/1.1/2.0
span.set_attribute("http.host", "localhost") # Host or main domain name: api.mycompany.com
span.set_attribute("http.target", "/stage-test-client1?param1=10") # URL target (path + query)
span.set_attribute("net.sock.peer.addr", "127.0.0.1") # IP address of incoming request
span.set_attribute("http.status_code", 200) # HTTP status code: 200/404/500/etc
# Headers (as JSON strings)
span.set_attribute("http.request.headers", request_headers_json)
span.set_attribute("http.response.headers", response_headers_json)
# Request and response bodies
span.set_attribute("http.request.body", req_body)
span.set_attribute("http.response.body", resp_body)
# Perform your actual HTTP request logic here
# ...
# Set span status
span.set_status(Status(StatusCode.OK))Complete code example (including initializing otel sdk and sending the span)
Replace the following variable in the code piece
Variable | Description | Example Value |
| grpc endpoint of Astra Traffic Collector. Provide the endpoint address without scheme |
|
| integration ID aka |
|
package main
import (
"context"
"encoding/json"
"log"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/resource"
"go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
)
// Global configuration
var (
OtelEndpoint = "astra-traffic-collector:4317"
SensorID = "17062dc8-6cd6-4951-9b21-5f40c85b0e71"
SensorVersion = "1.0.0"
ServiceName = "astra-otel-sdk"
)
func initTracer() (*trace.TracerProvider, error) {
// Create resource with required attributes
res := resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(ServiceName),
attribute.String("sensor.version", SensorVersion),
)
// Create OTLP exporter
exporter, err := otlptracegrpc.New(context.Background(),
otlptracegrpc.WithEndpoint(OtelEndpoint),
otlptracegrpc.WithInsecure(),
)
if err != nil {
return nil, err
}
// Create tracer provider
tp := trace.NewTracerProvider(
trace.WithBatcher(exporter),
trace.WithResource(res),
)
// Set global tracer provider
otel.SetTracerProvider(tp)
return tp, nil
}
func createSampleSpan() {
tracer := otel.Tracer("user-api-service")
// Sample request/response data
requestBody := `{
"name": "John Doe",
"email": "john.doe@example.com",
"age": 30,
"preferences": {
"theme": "dark",
"notifications": true
}
}`
responseBody := `{
"id": 12345,
"name": "John Doe",
"email": "john.doe@example.com",
"created_at": "2023-12-01T10:30:00Z",
"status": "active"
}`
// Request headers
requestHeaders := map[string]string{
"host": "api.example.com",
"content-type": "application/json",
"accept": "application/json",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user-agent": "MyApp/1.0",
}
// Response headers
responseHeaders := map[string]string{
"content-type": "application/json",
"content-length": "156",
"x-request-id": "req-123456789",
"cache-control": "no-cache",
}
// Convert headers to JSON
requestHeadersJSON, err := json.Marshal(requestHeaders)
if err != nil {
log.Printf("Failed to marshal request headers: %v", err)
return
}
responseHeadersJSON, err := json.Marshal(responseHeaders)
if err != nil {
log.Printf("Failed to marshal response headers: %v", err)
return
}
// Create span
ctx, span := tracer.Start(context.Background(), "http-request")
defer span.End()
// Set all required span attributes
span.SetAttributes(
// HTTP Request Metadata
attribute.String("sensor.id", SensorID),
attribute.String("http.method", "POST"),
attribute.String("http.scheme", "https"),
attribute.String("http.flavor", "1.1"),
attribute.String("http.host", "api.example.com"),
attribute.String("http.target", "/users?source=mobile"),
attribute.Int("http.status_code", 200),
// Network Information
attribute.String("net.sock.peer.addr", "192.168.1.100"),
// Headers and Body
attribute.String("http.request.headers", string(requestHeadersJSON)),
attribute.String("http.response.headers", string(responseHeadersJSON)),
attribute.String("http.request.body", requestBody),
attribute.String("http.response.body", responseBody),
)
log.Printf("Created span with ID: %s", span.SpanContext().SpanID().String())
// Simulate some processing time
time.Sleep(100 * time.Millisecond)
}
func main() {
// Initialize tracer
tp, err := initTracer()
if err != nil {
log.Fatalf("Failed to initialize tracer: %v", err)
}
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
log.Printf("Error shutting down tracer provider: %v", err)
}
}()
// Create and send sample span
createSampleSpan()
// Wait a bit for export
time.Sleep(2 * time.Second)
log.Println("Sample span sent successfully")
}import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class OtelTracingExample {
// Global configuration
private static final String OTEL_ENDPOINT = "astra-traffic-collector:4317";
private static final String SENSOR_ID = "17062dc8-6cd6-4951-9b21-5f40c85b0e71";
private static final String SENSOR_VERSION = "1.0.0";
private static final String SERVICE_NAME = "user-api-service";
private static OpenTelemetry openTelemetry;
private static ObjectMapper objectMapper = new ObjectMapper();
public static void initTracer() {
// Create resource with required attributes
Resource resource = Resource.getDefault()
.merge(Resource.create(Attributes.builder()
.put(ResourceAttributes.SERVICE_NAME, SERVICE_NAME)
.put(AttributeKey.stringKey("sensor.version"), SENSOR_VERSION)
.build()));
// Create OTLP exporter
OtlpGrpcSpanExporter spanExporter = OtlpGrpcSpanExporter.builder()
.setEndpoint(OTEL_ENDPOINT)
.build();
// Create tracer provider
SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
.setResource(resource)
.addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build())
.build();
// Build OpenTelemetry SDK
openTelemetry = OpenTelemetrySdk.builder()
.setTracerProvider(sdkTracerProvider)
.buildAndRegisterGlobal();
}
public static void createSampleSpan() {
Tracer tracer = openTelemetry.getTracer("user-api-service");
// Sample request/response data
String requestBody = "{\n" +
" \"name\": \"John Doe\",\n" +
" \"email\": \"john.doe@example.com\",\n" +
" \"age\": 30,\n" +
" \"preferences\": {\n" +
" \"theme\": \"dark\",\n" +
" \"notifications\": true\n" +
" }\n" +
"}";
String responseBody = "{\n" +
" \"id\": 12345,\n" +
" \"name\": \"John Doe\",\n" +
" \"email\": \"john.doe@example.com\",\n" +
" \"created_at\": \"2023-12-01T10:30:00Z\",\n" +
" \"status\": \"active\"\n" +
"}";
// Request headers
Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("host", "api.example.com");
requestHeaders.put("content-type", "application/json");
requestHeaders.put("accept", "application/json");
requestHeaders.put("authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...");
requestHeaders.put("user-agent", "MyApp/1.0");
// Response headers
Map<String, String> responseHeaders = new HashMap<>();
responseHeaders.put("content-type", "application/json");
responseHeaders.put("content-length", "156");
responseHeaders.put("x-request-id", "req-123456789");
responseHeaders.put("cache-control", "no-cache");
try {
// Convert headers to JSON
String requestHeadersJson = objectMapper.writeValueAsString(requestHeaders);
String responseHeadersJson = objectMapper.writeValueAsString(responseHeaders);
// Create span
Span span = tracer.spanBuilder("http-request").startSpan();
try {
// Set all required span attributes
span.setAttribute("sensor.id", SENSOR_ID);
span.setAttribute("http.method", "POST");
span.setAttribute("http.scheme", "https");
span.setAttribute("http.flavor", "1.1");
span.setAttribute("http.host", "api.example.com");
span.setAttribute("http.target", "/users?source=mobile");
span.setAttribute("http.status_code", 200);
span.setAttribute("net.sock.peer.addr", "192.168.1.100");
span.setAttribute("http.request.headers", requestHeadersJson);
span.setAttribute("http.response.headers", responseHeadersJson);
span.setAttribute("http.request.body", requestBody);
span.setAttribute("http.response.body", responseBody);
System.out.println("Created span with ID: " + span.getSpanContext().getSpanId());
// Simulate some processing time
Thread.sleep(100);
} finally {
span.end();
}
} catch (Exception e) {
System.err.println("Error creating span: " + e.getMessage());
}
}
public static void main(String[] args) {
try {
// Initialize tracer
initTracer();
// Create and send sample span
createSampleSpan();
// Wait a bit for export
Thread.sleep(2000);
System.out.println("Sample span sent successfully");
// Shutdown
GlobalOpenTelemetry.get().close();
} catch (Exception e) {
System.err.println("Error in main: " + e.getMessage());
}
}
}using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
namespace OtelTracingExample
{
public static class OtelConfig
{
public const string OtelEndpoint = "astra-traffic-collector:4317";
public const string SensorID = "17062dc8-6cd6-4951-9b21-5f40c85b0e71";
public const string SensorVersion = "1.0.0";
public const string ServiceName = "user-api-service";
}
public static class Instrumentation
{
public static readonly ActivitySource ActivitySource = new ActivitySource("user-api-service");
}
public static class Program
{
private static TracerProvider tracerProvider;
public static void InitTracer()
{
// Create tracer provider with resource attributes
tracerProvider = Sdk.CreateTracerProviderBuilder()
.SetResourceBuilder(ResourceBuilder.CreateDefault()
.AddService(OtelConfig.ServiceName)
.AddAttributes(new KeyValuePair<string, object>[]
{
new("sensor.version", OtelConfig.SensorVersion)
}))
.AddSource("user-api-service")
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri(OtelConfig.OtelEndpoint);
options.Protocol = OtlpExportProtocol.Grpc;
})
.Build();
}
public static void CreateSampleSpan()
{
// Sample request/response data
string requestBody = @"{
""name"": ""John Doe"",
""email"": ""john.doe@example.com"",
""age"": 30,
""preferences"": {
""theme"": ""dark"",
""notifications"": true
}
}";
string responseBody = @"{
""id"": 12345,
""name"": ""John Doe"",
""email"": ""john.doe@example.com"",
""created_at"": ""2023-12-01T10:30:00Z"",
""status"": ""active""
}";
// Request headers
var requestHeaders = new Dictionary<string, string>
{
{ "host", "api.example.com" },
{ "content-type", "application/json" },
{ "accept", "application/json" },
{ "authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." },
{ "user-agent", "MyApp/1.0" }
};
// Response headers
var responseHeaders = new Dictionary<string, string>
{
{ "content-type", "application/json" },
{ "content-length", "156" },
{ "x-request-id", "req-123456789" },
{ "cache-control", "no-cache" }
};
// Convert headers to JSON
string requestHeadersJson = JsonSerializer.Serialize(requestHeaders);
string responseHeadersJson = JsonSerializer.Serialize(responseHeaders);
// Create span
using var activity = Instrumentation.ActivitySource.StartActivity("http-request");
if (activity != null)
{
// Set all required span attributes
activity.SetTag("sensor.id", SensorID);
activity.SetTag("http.method", "POST");
activity.SetTag("http.scheme", "https");
activity.SetTag("http.flavor", "1.1");
activity.SetTag("http.host", "api.example.com");
activity.SetTag("http.target", "/users?source=mobile");
activity.SetTag("http.status_code", 200);
activity.SetTag("net.sock.peer.addr", "192.168.1.100");
activity.SetTag("http.request.headers", requestHeadersJson);
activity.SetTag("http.response.headers", responseHeadersJson);
activity.SetTag("http.request.body", requestBody);
activity.SetTag("http.response.body", responseBody);
Console.WriteLine($"Created span with ID: {activity.Id}");
// Simulate some processing time
Thread.Sleep(100);
}
}
public static async Task Main(string[] args)
{
try
{
// Initialize tracer
InitTracer();
// Create and send sample span
CreateSampleSpan();
// Wait a bit for export
await Task.Delay(2000);
Console.WriteLine("Sample span sent successfully");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
// Shutdown
tracerProvider?.Dispose();
Instrumentation.ActivitySource?.Dispose();
}
}
}
}const { NodeSDK } = require('@opentelemetry/sdk-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-otlp-grpc');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const { trace } = require('@opentelemetry/api');
// Global configuration
const OTEL_ENDPOINT = 'astra-traffic-collector:4317';
const SENSOR_ID = '17062dc8-6cd6-4951-9b21-5f40c85b0e71'
const SENSOR_VERSION = '1.0.0';
const SERVICE_NAME = 'user-api-service';
let sdk;
function initTracer() {
// Initialize OpenTelemetry SDK
sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: SERVICE_NAME,
'sensor.version': SENSOR_VERSION
}),
traceExporter: new OTLPTraceExporter({
url: OTEL_ENDPOINT
})
});
// Start the SDK
sdk.start();
console.log('OpenTelemetry initialized successfully');
}
function createSampleSpan() {
const tracer = trace.getTracer('user-api-service');
// Sample request/response data
const requestBody = `{
"name": "John Doe",
"email": "john.doe@example.com",
"age": 30,
"preferences": {
"theme": "dark",
"notifications": true
}
}`;
const responseBody = `{
"id": 12345,
"name": "John Doe",
"email": "john.doe@example.com",
"created_at": "2023-12-01T10:30:00Z",
"status": "active"
}`;
// Request headers
const requestHeaders = {
"host": "api.example.com",
"content-type": "application/json",
"accept": "application/json",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user-agent": "MyApp/1.0"
};
// Response headers
const responseHeaders = {
"content-type": "application/json",
"content-length": "156",
"x-request-id": "req-123456789",
"cache-control": "no-cache"
};
// Convert headers to JSON
const requestHeadersJson = JSON.stringify(requestHeaders);
const responseHeadersJson = JSON.stringify(responseHeaders);
// Create span
const span = tracer.startSpan('http-request');
try {
// Set all required span attributes
span.setAttributes({
'sensor.id': '17062dc8-6cd6-4951-9b21-5f40c85b0e71',
'http.method': 'POST',
'http.scheme': 'https',
'http.flavor': '1.1',
'http.host': 'api.example.com',
'http.target': '/users?source=mobile',
'http.status_code': 200,
'net.sock.peer.addr': '192.168.1.100',
'http.request.headers': requestHeadersJson,
'http.response.headers': responseHeadersJson,
'http.request.body': requestBody,
'http.response.body': responseBody
});
console.log(`Created span with trace ID: ${span.spanContext().traceId}`);
// Simulate some processing time
setTimeout(() => {}, 100);
} finally {
span.end();
}
}
async function main() {
try {
// Initialize tracer
initTracer();
// Create and send sample span
createSampleSpan();
// Wait a bit for export
await new Promise(resolve => setTimeout(resolve, 2000));
console.log('Sample span sent successfully');
} catch (error) {
console.error('Error:', error.message);
} finally {
// Shutdown
if (sdk) {
await sdk.shutdown();
}
}
}
// Required dependencies in package.json:
/*
{
"dependencies": {
"@opentelemetry/api": "^1.4.0",
"@opentelemetry/sdk-node": "^0.39.0",
"@opentelemetry/exporter-otlp-grpc": "^0.39.0",
"@opentelemetry/semantic-conventions": "^1.12.0"
}
}
*/
main().catch(console.error);import json
import time
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.trace import Status, StatusCode
# Global configuration
OTEL_ENDPOINT = "astra-traffic-collector:4317"
SENSOR_ID = "17062dc8-6cd6-4951-9b21-5f40c85b0e71"
SENSOR_VERSION = "1.0.0"
SERVICE_NAME = "user-api-service"
def init_tracer():
"""Initialize OpenTelemetry tracer with proper configuration."""
# Create resource with required attributes
resource = Resource.create({
"service.name": SERVICE_NAME,
"sensor.version": SENSOR_VERSION
})
# Set tracer provider
trace.set_tracer_provider(TracerProvider(resource=resource))
# Configure OTLP exporter
otlp_exporter = OTLPSpanExporter(endpoint=OTEL_ENDPOINT)
span_processor = BatchSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)
print("OpenTelemetry initialized successfully")
def create_sample_span():
"""Create a sample span with all required attributes."""
tracer = trace.get_tracer("user-api-service")
# Sample request/response data
request_body = """{
"name": "John Doe",
"email": "john.doe@example.com",
"age": 30,
"preferences": {
"theme": "dark",
"notifications": true
}
}"""
response_body = """{
"id": 12345,
"name": "John Doe",
"email": "john.doe@example.com",
"created_at": "2023-12-01T10:30:00Z",
"status": "active"
}"""
# Request headers
request_headers = {
"host": "api.example.com",
"content-type": "application/json",
"accept": "application/json",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user-agent": "MyApp/1.0"
}
# Response headers
response_headers = {
"content-type": "application/json",
"content-length": "156",
"x-request-id": "req-123456789",
"cache-control": "no-cache"
}
# Convert headers to JSON strings
try:
request_headers_json = json.dumps(request_headers, separators=(',', ':'))
response_headers_json = json.dumps(response_headers, separators=(',', ':'))
except Exception as e:
print(f"Error converting headers to JSON: {e}")
return
# Create span with all required attributes
with tracer.start_as_current_span("http-request") as span:
# Set all required span attributes
span.set_attribute("sensor.id", SENSOR_ID)
span.set_attribute("http.method", "POST")
span.set_attribute("http.scheme", "https")
span.set_attribute("http.flavor", "1.1")
span.set_attribute("http.host", "api.example.com")
span.set_attribute("http.target", "/users?source=mobile")
span.set_attribute("http.status_code", 200)
span.set_attribute("net.sock.peer.addr", "192.168.1.100")
span.set_attribute("http.request.headers", request_headers_json)
span.set_attribute("http.response.headers", response_headers_json)
span.set_attribute("http.request.body", request_body)
span.set_attribute("http.response.body", response_body)
print(f"Created span with trace ID: {span.get_span_context().trace_id}")
# Simulate some processing time
time.sleep(0.1)
# Set span status
span.set_status(Status(StatusCode.OK))
def main():
"""Main function to run the example."""
try:
# Initialize tracer
init_tracer()
# Create and send sample span
create_sample_span()
# Wait a bit for export
time.sleep(2)
print("Sample span sent successfully")
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()Implementation Notes
Define global configuration variables:
var ( OtelEndpoint = "astra-traffic-collector:4317" SensorID = "17062dc8-6cd6-4951-9b21-5f40c85b0e71" SensorVersion = "1.0.0" ServiceName = "astra-otel-sdk" )public class OtelConfig { // Global configuration variables public static final String OTEL_ENDPOINT = "astra-traffic-collector:4317"; private static final String SENSOR_ID = "17062dc8-6cd6-4951-9b21-5f40c85b0e71"; public static final String SENSOR_VERSION = "1.0.0"; // Other configuration values can be added here }public static class OtelConfig { // Global configuration variables public const string OtelEndpoint = "astra-traffic-collector:4317"; public const string SensorID = "17062dc8-6cd6-4951-9b21-5f40c85b0e71"; public const string SensorVersion = "1.0.0"; // Other configuration values can be added here }// otel-config.js class OtelConfig { static get OTEL_ENDPOINT() { return process.env.OTEL_ENDPOINT || 'astra-traffic-collector:4317'; } static get SENSOR_ID() { return process.env.SENSOR_ID || '17062dc8-6cd6-4951-9b21-5f40c85b0e71'; } static get SERVICE_NAME() { return process.env.SERVICE_NAME || 'your-service-name'; } } module.exports = { OtelConfig };# otel_config.py import os class OtelConfig: """Global configuration for OpenTelemetry settings""" OTEL_ENDPOINT = os.getenv("OTEL_ENDPOINT", "astra-traffic-collector:4317") SENSOR_ID = os.getenv("SENSOR_ID","17062dc8-6cd6-4951-9b21-5f40c85b0e71") SENSOR_VERSION = os.getenv("SENSOR_VERSION", "1.0.0") SERVICE_NAME = os.getenv("SERVICE_NAME", "your-service-name")Set resource attributes at tracer initialization:
res := resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("your-service-name"), attribute.String("sensor.version", SensorVersion), )// Initialize OpenTelemetry Resource resource = Resource.getDefault() .merge(Resource.create(Attributes.builder() .put("service.name", "your-service-name") .put("sensor.version", OtelConfig.SENSOR_VERSION) .build())); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .setResource(resource) .addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder() .setEndpoint(OtelConfig.OTEL_ENDPOINT) .build()).build()) .build(); OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .buildAndRegisterGlobal(); Tracer tracer = openTelemetry.getTracer("com.example.YourApplicationName");using OpenTelemetry; using OpenTelemetry.Resources; using OpenTelemetry.Trace; using Microsoft.Extensions.DependencyInjection; // For ASP.NET Core application in Startup.cs or Program.cs public void ConfigureServices(IServiceCollection services) { services.AddOpenTelemetry() .WithTracing(builder => builder .SetResourceBuilder(ResourceBuilder.CreateDefault() .AddService("your-service-name") .AddAttributes(new KeyValuePair<string, object>[] { new("sensor.version", OtelConfig.SensorVersion) })) .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddOtlpExporter(options => { options.Endpoint = new Uri(OtelConfig.OtelEndpoint); }) ); } // For a non-ASP.NET Core application, you can initialize the provider directly: static TracerProvider CreateTracerProvider() { return Sdk.CreateTracerProviderBuilder() .SetResourceBuilder(ResourceBuilder.CreateDefault() .AddService("your-service-name") .AddAttributes(new KeyValuePair<string, object>[] { new("sensor.version", OtelConfig.SensorVersion) })) .AddSource("your-application-name") .AddOtlpExporter(options => { options.Endpoint = new Uri(OtelConfig.OtelEndpoint); }) .Build(); }const { NodeSDK } = require('@opentelemetry/sdk-node'); const { OTLPTraceExporter } = require('@opentelemetry/exporter-otlp-http'); const { Resource } = require('@opentelemetry/resources'); const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions'); const { OtelConfig } = require('./otel-config'); // Initialize OpenTelemetry const sdk = new NodeSDK({ resource: new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: OtelConfig.SERVICE_NAME, 'sensor.version': OtelConfig.SENSOR_VERSION }), traceExporter: new OTLPTraceExporter({ url: OtelConfig.OTEL_ENDPOINT }) }); // Initialize the SDK sdk.start(); // Get tracer after initialization const { trace } = require('@opentelemetry/api'); const tracer = trace.getTracer('com.example.YourApplicationName'); module.exports = { tracer };from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter from opentelemetry.sdk.resources import Resource from otel_config import OtelConfig # Initialize OpenTelemetry resource = Resource.create({ "service.name": OtelConfig.SERVICE_NAME, "sensor.version": OtelConfig.SENSOR_VERSION }) trace.set_tracer_provider(TracerProvider(resource=resource)) # Configure OTLP exporter otlp_exporter = OTLPSpanExporter(endpoint=OtelConfig.OTEL_ENDPOINT) span_processor = BatchSpanProcessor(otlp_exporter) trace.get_tracer_provider().add_span_processor(span_processor) # Get tracer tracer = trace.get_tracer("com.example.YourApplicationName")Ensure span attributes include all required metadata.
Redact sensitive values (tokens, passwords, API keys) from headers and bodies.
Best Practices
Instrument at the handler layer, not the client layer
Place tracing logic inside your REST controllers or HTTP request handlers — where both the full request and response are available. Instrumenting at the HTTP client layer risks missing response details needed for accurate span attributes.
Trace only successful requests
Only include spans for 2xx and 3xx responses. Tracing 4xx and 5xx responses adds noise to your API inventory without reflecting valid service interactions. Astra will not process spans outside this range.
Redact sensitive header values before setting span attributes
Strip or mask sensitive headers — such as Authorization, Cookie, X-API-Key, and any session tokens — before passing them to http.request.headers or http.response.headers. Astra does not perform automatic redaction, so this is the developer's responsibility.
Cap body size for large payloads
Capturing full request and response bodies for large payloads can increase memory usage and export time. Set a size limit in your instrumentation logic — truncating or omitting bodies beyond a defined threshold (e.g. 10KB) is a reasonable default.
Use environment variables for configuration
Avoid hardcoding OtelEndpoint and SensorID in your application code. Load them from environment variables so they can be updated across environments without code changes.
Keep resource attributes consistent across services
The sensor.version and service.name resource attributes should follow a consistent naming convention across all instrumented services. This ensures spans are correctly grouped and attributed in the Astra dashboard.
Align attribute names with OpenTelemetry Semantic Conventions
Where possible, use the standard OTel attribute names (http.method, http.status_code, etc.) as specified in this guide. Deviating from these will cause spans to be malformed or dropped by the collector.