google-site-verification: google61fe8ba583a51912.html
Lightstep Java SDK: Distributed Tracing and Observability Implementation

Lightstep is a powerful distributed tracing system that helps you understand and optimize your microservices architecture. This guide covers comprehensive integration of Lightstep's Java SDK for end-to-end observability.


Dependencies and Setup

Maven Dependencies
<properties> <lightstep.version>0.21.0</lightstep.version> <opentelemetry.version>1.28.0</opentelemetry.version> <grpc.version>1.56.1</grpc.version> </properties> <dependencies> <!-- Lightstep OpenTelemetry SDK --> <dependency> <groupId>com.lightstep</groupId> <artifactId>lightstep-opentelemetry-java</artifactId> <version>${lightstep.version}</version> </dependency> <!-- OpenTelemetry API --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> <version>${opentelemetry.version}</version> </dependency> <!-- OpenTelemetry SDK --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk</artifactId> <version>${opentelemetry.version}</version> </dependency> <!-- OpenTelemetry Exporter --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-otlp</artifactId> <version>${opentelemetry.version}</version> </dependency> <!-- gRPC for OTLP export --> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>${grpc.version}</version> </dependency> <!-- Spring Boot Starter (if using Spring) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.1.0</version> </dependency> </dependencies>

Core Configuration

1. Lightstep Tracer Configuration
package com.example.tracing.config; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.propagation.ContextPropagators; 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.ResourceAttributes; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; @Configuration public class LightstepConfig { @Value("${lightstep.access.token:}") private String lightstepAccessToken; @Value("${lightstep.service.name:unknown-service}") private String serviceName; @Value("${lightstep.collector.host:ingest.lightstep.com}") private String collectorHost; @Value("${lightstep.collector.port:443}") private int collectorPort; @Bean public OpenTelemetry openTelemetry() { // Create resource with service name Resource resource = Resource.getDefault() .merge(Resource.builder() .put(ResourceAttributes.SERVICE_NAME, serviceName) .put(ResourceAttributes.SERVICE_VERSION, "1.0.0") .put(ResourceAttributes.DEPLOYMENT_ENVIRONMENT, getEnvironment()) .build()); // Configure OTLP exporter for Lightstep OtlpGrpcSpanExporter spanExporter = OtlpGrpcSpanExporter.builder() .setEndpoint(String.format("https://%s:%d", collectorHost, collectorPort)) .addHeader("lightstep-access-token", lightstepAccessToken) .setTimeout(30, TimeUnit.SECONDS) .build(); // Configure SDK SdkTracerProvider tracerProvider = SdkTracerProvider.builder() .addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build()) .setResource(resource) .build(); OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) .build(); // Register as global instance GlobalOpenTelemetry.set(openTelemetrySdk); return openTelemetrySdk; } @Bean public Tracer tracer(OpenTelemetry openTelemetry) { return openTelemetry.getTracer(serviceName, "1.0.0"); } private String getEnvironment() { return System.getenv().getOrDefault("ENVIRONMENT", "development"); } }
2. Environment-Specific Configuration
# application.yml lightstep: access: token: ${LIGHTSTEP_ACCESS_TOKEN:} service: name: user-service collector: host: ingest.lightstep.com port: 443 management: tracing: sampling: probability: 1.0
// Alternative Programmatic Configuration public class LightstepTracerFactory { public static OpenTelemetry createLightstepTracer(String serviceName, String accessToken, String environment) { // Using Lightstep's OpenTelemetry distribution return LightstepOpenTelemetry .newBuilder(accessToken) .setServiceName(serviceName) .setServiceVersion("1.0.0") .setCollectorHost("ingest.lightstep.com") .setCollectorPort(443) .setCollectorProtocol("https") .setResourceAttributes(ResourceAttributes.builder() .put("deployment.environment", environment) .put("team", "backend") .build()) .build() .getOpenTelemetry(); } }

Instrumentation Implementation

1. HTTP Request Filter for Incoming Requests
package com.example.tracing.filter; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.TextMapGetter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class TracingFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(TracingFilter.class); private final Tracer tracer; public TracingFilter(Tracer tracer) { this.tracer = tracer; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; // Extract context from incoming headers Context extractedContext = GlobalOpenTelemetry.getPropagators().getTextMapPropagator() .extract(Context.current(), httpRequest, HttpServletRequestGetter.INSTANCE); // Start span Span span = tracer.spanBuilder(httpRequest.getMethod() + " " + httpRequest.getRequestURI()) .setSpanKind(SpanKind.SERVER) .setParent(extractedContext) .startSpan(); try (Scope scope = span.makeCurrent()) { // Add attributes to span span.setAttribute("http.method", httpRequest.getMethod()); span.setAttribute("http.route", httpRequest.getRequestURI()); span.setAttribute("http.url", httpRequest.getRequestURL().toString()); span.setAttribute("http.user_agent", httpRequest.getHeader("User-Agent")); span.setAttribute("http.client_ip", getClientIp(httpRequest)); // Set span as current chain.doFilter(request, response); // Record response status span.setAttribute("http.status_code", httpResponse.getStatus()); if (httpResponse.getStatus() >= 400) { span.setStatus(StatusCode.ERROR, "HTTP " + httpResponse.getStatus()); } else { span.setStatus(StatusCode.OK); } } catch (Exception e) { // Record exception span.recordException(e); span.setStatus(StatusCode.ERROR, e.getMessage()); throw e; } finally { span.end(); } } private String getClientIp(HttpServletRequest request) { String xForwardedFor = request.getHeader("X-Forwarded-For"); if (xForwardedFor != null && !xForwardedFor.isEmpty()) { return xForwardedFor.split(",")[0]; } return request.getRemoteAddr(); } private enum HttpServletRequestGetter implements TextMapGetter<HttpServletRequest> { INSTANCE; @Override public Iterable<String> keys(HttpServletRequest carrier) { return () -> carrier.getHeaderNames().asIterator(); } @Override public String get(HttpServletRequest carrier, String key) { return carrier.getHeader(key); } } }
2. RestTemplate Interceptor for Outgoing HTTP Calls
package com.example.tracing.interceptor; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.TextMapSetter; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component; import java.io.IOException; @Component public class TracingRestTemplateInterceptor implements ClientHttpRequestInterceptor { private final Tracer tracer; public TracingRestTemplateInterceptor(Tracer tracer) { this.tracer = tracer; } @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { // Create child span for outgoing request Span span = tracer.spanBuilder("HTTP " + request.getMethod() + " " + request.getURI().getHost()) .setSpanKind(SpanKind.CLIENT) .startSpan(); try (Scope scope = span.makeCurrent()) { // Add attributes span.setAttribute("http.method", request.getMethod().name()); span.setAttribute("http.url", request.getURI().toString()); span.setAttribute("http.target", request.getURI().getPath()); span.setAttribute("net.peer.name", request.getURI().getHost()); span.setAttribute("net.peer.port", request.getURI().getPort()); // Inject tracing headers GlobalOpenTelemetry.getPropagators().getTextMapPropagator() .inject(Context.current(), request, HttpRequestSetter.INSTANCE); // Execute request ClientHttpResponse response = execution.execute(request, body); // Record response span.setAttribute("http.status_code", response.getStatusCode().value()); if (response.getStatusCode().isError()) { span.setStatus(StatusCode.ERROR, "HTTP " + response.getStatusCode().value()); } return response; } catch (Exception e) { span.recordException(e); span.setStatus(StatusCode.ERROR, e.getMessage()); throw e; } finally { span.end(); } } private enum HttpRequestSetter implements TextMapSetter<HttpRequest> { INSTANCE; @Override public void set(HttpRequest carrier, String key, String value) { carrier.getHeaders().add(key, value); } } }
3. Database Tracing Aspect
package com.example.tracing.aspect; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.api.trace.Tracer; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.Arrays; @Aspect @Component public class DatabaseTracingAspect { private static final Logger logger = LoggerFactory.getLogger(DatabaseTracingAspect.class); private final Tracer tracer; public DatabaseTracingAspect(Tracer tracer) { this.tracer = tracer; } @Around("execution(* com.example.repository.*.*(..))") public Object traceRepositoryMethods(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); Span span = tracer.spanBuilder(className + "." + methodName) .setSpanKind(SpanKind.INTERNAL) .startSpan(); try (Scope scope = span.makeCurrent()) { // Add database operation attributes span.setAttribute("db.operation", methodName); span.setAttribute("db.component", "JDBC"); span.setAttribute("db.instance", "users-db"); // Log parameters (be careful with sensitive data) if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) { span.setAttribute("db.parameters.count", joinPoint.getArgs().length); } logger.debug("Executing database operation: {}.{}", className, methodName); Object result = joinPoint.proceed(); span.setStatus(StatusCode.OK); return result; } catch (Exception e) { span.recordException(e); span.setStatus(StatusCode.ERROR, e.getMessage()); logger.error("Database operation failed: {}.{}", className, methodName, e); throw e; } finally { span.end(); } } @Around("@annotation(com.example.tracing.annotation.TracedQuery)") public Object traceCustomQueries(ProceedingJoinPoint joinPoint) throws Throwable { String queryName = getQueryName(joinPoint); Span span = tracer.spanBuilder("DB Query: " + queryName) .setSpanKind(SpanKind.INTERNAL) .startSpan(); long startTime = System.currentTimeMillis(); try (Scope scope = span.makeCurrent()) { span.setAttribute("db.operation", "query"); span.setAttribute("db.query.name", queryName); span.setAttribute("db.system", "postgresql"); Object result = joinPoint.proceed(); long duration = System.currentTimeMillis() - startTime; span.setAttribute("db.duration_ms", duration); span.setStatus(StatusCode.OK); return result; } catch (Exception e) { span.recordException(e); span.setStatus(StatusCode.ERROR, e.getMessage()); throw e; } finally { span.end(); } } private String getQueryName(ProceedingJoinPoint joinPoint) { return joinPoint.getSignature().getName(); } }
4. Custom Business Operation Tracing
package com.example.tracing.service; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.api.trace.Tracer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; @Component public class BusinessOperationTracer { private static final Logger logger = LoggerFactory.getLogger(BusinessOperationTracer.class); private final Tracer tracer; public BusinessOperationTracer(Tracer tracer) { this.tracer = tracer; } public <T> T traceOperation(String operationName, Map<String, Object> attributes, BusinessOperation<T> operation) { Span span = tracer.spanBuilder(operationName) .setSpanKind(io.opentelemetry.api.trace.SpanKind.INTERNAL) .startSpan(); try (Scope scope = span.makeCurrent()) { // Set operation attributes if (attributes != null) { attributes.forEach((key, value) -> span.setAttribute(key, String.valueOf(value))); } span.setAttribute("business.operation", operationName); span.setAttribute("component", "business-logic"); logger.info("Starting business operation: {}", operationName); T result = operation.execute(); span.setStatus(StatusCode.OK); logger.info("Business operation completed: {}", operationName); return result; } catch (Exception e) { span.recordException(e); span.setStatus(StatusCode.ERROR, e.getMessage()); logger.error("Business operation failed: {}", operationName, e); throw e; } finally { span.end(); } } public void traceAsyncOperation(String operationName, Map<String, Object> attributes, Runnable operation) { // Capture current context for async execution Context context = Context.current(); new Thread(() -> { try (Scope scope = context.makeCurrent()) { traceOperation(operationName, attributes, () -> { operation.run(); return null; }); } }).start(); } @FunctionalInterface public interface BusinessOperation<T> { T execute(); } }

Service Implementation Examples

1. User Service with Comprehensive Tracing
package com.example.service; import com.example.tracing.service.BusinessOperationTracer; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; @Service public class UserService { private static final Logger logger = LoggerFactory.getLogger(UserService.class); private final Tracer tracer; private final BusinessOperationTracer businessTracer; private final UserRepository userRepository; public UserService(Tracer tracer, BusinessOperationTracer businessTracer, UserRepository userRepository) { this.tracer = tracer; this.businessTracer = businessTracer; this.userRepository = userRepository; } public User createUser(CreateUserRequest request) { return businessTracer.traceOperation("user.create", Map.of( "user.email", request.getEmail(), "user.role", request.getRole(), "user.source", request.getSource() ), () -> { // Validate user validateUserRequest(request); // Check for existing user if (userRepository.existsByEmail(request.getEmail())) { throw new UserAlreadyExistsException("User already exists"); } // Create user entity User user = new User(request); User savedUser = userRepository.save(user); // Add event to current span Span.current().addEvent("user.created", io.opentelemetry.api.common.Attributes.of( io.opentelemetry.api.common.AttributeKey.stringKey("user.id"), savedUser.getId() )); logger.info("User created successfully: {}", savedUser.getId()); return savedUser; }); } public User findUserById(String userId) { Span span = tracer.spanBuilder("user.findById") .setAttribute("user.id", userId) .startSpan(); try (Scope scope = span.makeCurrent()) { logger.debug("Searching for user: {}", userId); User user = userRepository.findById(userId) .orElseThrow(() -> new UserNotFoundException("User not found")); span.setAttribute("user.found", true); span.setAttribute("user.email", user.getEmail()); return user; } catch (Exception e) { span.recordException(e); span.setAttribute("user.found", false); throw e; } finally { span.end(); } } public void processUserBatch(List<String> userIds) { businessTracer.traceAsyncOperation("user.batchProcessing", Map.of("batch.size", userIds.size()), () -> { // Process users in batch userIds.forEach(this::processSingleUser); }); } private void processSingleUser(String userId) { // This will be traced as part of the parent async operation User user = findUserById(userId); // Process user... } private void validateUserRequest(CreateUserRequest request) { if (request.getEmail() == null || !request.getEmail().contains("@")) { throw new IllegalArgumentException("Invalid email address"); } } }
2. Order Service with Distributed Tracing
package com.example.service; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.Map; @Service public class OrderService { private final Tracer tracer; private final RestTemplate restTemplate; private final BusinessOperationTracer businessTracer; public OrderService(Tracer tracer, RestTemplate restTemplate, BusinessOperationTracer businessTracer) { this.tracer = tracer; this.restTemplate = restTemplate; this.businessTracer = businessTracer; } public Order processOrder(OrderRequest request) { return businessTracer.traceOperation("order.process", Map.of( "order.amount", request.getAmount(), "order.currency", request.getCurrency(), "order.customer_id", request.getCustomerId() ), () -> { // Create order span Span orderSpan = Span.current(); // Step 1: Validate inventory boolean inventoryAvailable = checkInventory(request.getItems()); orderSpan.setAttribute("order.inventory_available", inventoryAvailable); if (!inventoryAvailable) { throw new InventoryUnavailableException("Insufficient inventory"); } // Step 2: Process payment PaymentResponse paymentResponse = processPayment(request); orderSpan.setAttribute("order.payment_id", paymentResponse.getPaymentId()); orderSpan.addEvent("payment.processed"); // Step 3: Create order Order order = createOrderEntity(request, paymentResponse); Order savedOrder = orderRepository.save(order); // Step 4: Update inventory updateInventory(request.getItems()); orderSpan.addEvent("inventory.updated"); // Step 5: Send notification sendOrderConfirmation(savedOrder); orderSpan.addEvent("notification.sent"); logger.info("Order processed successfully: {}", savedOrder.getId()); return savedOrder; }); } private boolean checkInventory(List<OrderItem> items) { Span span = tracer.spanBuilder("inventory.check") .setAttribute("inventory.items_count", items.size()) .startSpan(); try (Scope scope = span.makeCurrent()) { // Call inventory service InventoryCheckRequest checkRequest = new InventoryCheckRequest(items); ResponseEntity<InventoryCheckResponse> response = restTemplate.postForEntity( "http://inventory-service/api/inventory/check", checkRequest, InventoryCheckResponse.class ); boolean available = response.getBody().isAvailable(); span.setAttribute("inventory.available", available); return available; } finally { span.end(); } } private PaymentResponse processPayment(OrderRequest request) { return businessTracer.traceOperation("payment.process", Map.of( "payment.amount", request.getAmount(), "payment.currency", request.getCurrency() ), () -> { // Call payment service PaymentRequest paymentRequest = new PaymentRequest(request); ResponseEntity<PaymentResponse> response = restTemplate.postForEntity( "http://payment-service/api/payments/process", paymentRequest, PaymentResponse.class ); if (!response.getStatusCode().is2xxSuccessful()) { throw new PaymentProcessingException("Payment processing failed"); } return response.getBody(); }); } }

Configuration Management

1. Spring Boot Auto-Configuration
package com.example.tracing.config; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.spring.webmvc.v6_0.SpringWebMvcTelemetry; import jakarta.servlet.Filter; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnProperty(name = "lightstep.enabled", havingValue = "true", matchIfMissing = true) public class TracingAutoConfiguration { @Bean public Filter tracingFilter(OpenTelemetry openTelemetry) { return SpringWebMvcTelemetry.create(openTelemetry).createServletFilter(); } }
2. Environment-Specific Configuration
# application-dev.yml lightstep: enabled: true access: token: ${LIGHTSTEP_DEV_TOKEN} service: name: user-service-dev collector: host: ingest.lightstep.com port: 443 opentelemetry: tracer: config: sampler: always_on export: interval: 5000 logging: level: io.opentelemetry: INFO
# application-prod.yml lightstep: enabled: true access: token: ${LIGHTSTEP_PROD_TOKEN} service: name: user-service collector: host: ingest.lightstep.com port: 443 opentelemetry: tracer: config: sampler: parentbased_always_on export: interval: 1000 management: endpoints: web: exposure: include: health,info,metrics

Testing and Verification

1. Unit Test for Tracing
package com.example.tracing.test; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension; import io.opentelemetry.sdk.trace.data.SpanData; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @SpringBootTest class UserServiceTracingTest { @RegisterExtension static final OpenTelemetryExtension otelTesting = OpenTelemetryExtension.create(); @Autowired private UserService userService; @Test void shouldCreateSpanForUserCreation() { // Given CreateUserRequest request = new CreateUserRequest("[email protected]", "USER"); // When User user = userService.createUser(request); // Then List<SpanData> spans = otelTesting.getSpans(); assertEquals(1, spans.size()); SpanData span = spans.get(0); assertEquals("user.create", span.getName()); assertEquals("[email protected]", span.getAttributes().get(AttributeKey.stringKey("user.email"))); } }
2. Integration Test
package com.example.tracing.test; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.ResponseEntity; import static org.junit.jupiter.api.Assertions.assertEquals; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class TracingIntegrationTest { @LocalServerPort private int port; private TestRestTemplate restTemplate = new TestRestTemplate(); @Test void shouldPropagateTraceHeaders() { // When ResponseEntity<String> response = restTemplate.getForEntity( "http://localhost:" + port + "/api/users/123", String.class); // Then assertEquals(200, response.getStatusCodeValue()); // Verify trace headers are present in response assertNotNull(response.getHeaders().get("traceparent")); } }

Best Practices

  1. Consistent Naming: Use consistent span names and attribute keys
  2. Reasonable Sampling: Adjust sampling rate based on traffic
  3. Meaningful Attributes: Include business context in spans
  4. Error Handling: Always record exceptions and set appropriate status
  5. Performance: Use async operations for non-critical tracing
  6. Security: Never include sensitive data in span attributes
// Good practice - meaningful attributes span.setAttribute("business.customer_tier", customer.getTier()); span.setAttribute("business.order_priority", order.getPriority()); // Bad practice - unclear attributes span.setAttribute("data", customerData);

Conclusion

Lightstep Java SDK provides:

  • Distributed tracing across microservices
  • Performance monitoring with detailed span timing
  • Error analysis with stack traces and context
  • Dependency mapping through span relationships
  • Business context in operational data

By implementing comprehensive tracing with Lightstep, you gain deep visibility into your application's behavior, enabling faster debugging, performance optimization, and better understanding of system dependencies.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper