skip to Main Content

I’m pretty new to OpenTelemetry world.

We have a PHP backend and Node.js Express server for SSR. For starters we want to collect tracing data for some subset of the requests. So, when a request comes to PHP server it calculates some chances, and if it is true it starts collecting tracing data from PHP side. Then comes to a point that it makes a HTTP request to Express server to render some JS and HTML. And the option of if the tracing is enabled or not is passed to Express server endpoint via PHP.

The problem:
Tracing is setup since the beginning of the Express server, to track everything correctly. But the option to stop tracing or not, comes from backend to Express server in a route. So, when the route is invoked I need to check the body and decide to stop tracing or not.

I can’t do it with samplers. Because like I said the decision to not send any tracing comes to the endpoint. Not since the beginning.

How can I stop the tracing and don’t send anything? Even if some data were already collected.

2

Answers


  1. In OpenTelemetry, the signal between services on whether to trace or not is called Context Propagation and should be propagated between services (in your case, from PHP to Express) via the W3C traceparent header [1].
    This header contains the parent trace id, span id, flags which indicate whether the parent is sampled or not. This is how traces across your two different services can be linked together (by having the same trace id, and having the root Express span linked to the PHP span which initiated the http request).

    What you are describing, where PHP makes the decision to trace (via sampling), and Express should only trace based on PHP’s decision, can be handled by a "Parent-based, always-off" sampler in Express, and probably a "Parent-based, trace id ratio-based" sampler in PHP. You won’t need to discard any data from Express, since it will only trace if the parent was traced.

    Lastly, you need to ensure that your outbound HTTP requests from PHP inject the appropriate traceparent header:

    $request = new Request('GET', 'express-app/some/route');
    $carrier = [];
    TraceContextPropagator::getInstance()->inject($carrier);
    foreach ($carrier as $name => $value) {
        $request = $request->withAddedHeader($name, $value);
    }
    $request->send();
    

    [1] https://www.w3.org/TR/trace-context/#traceparent-header

    Login or Signup to reply.
  2. Disabling OpenTelemetry tracing after it has been initialized is not a straightforward task because OpenTelemetry is designed to be initialized and configured at the start of an application and remain consistent throughout its lifetime. However, you can manage the behavior of tracing dynamically to some extent using different strategies.

    Here’s a general approach you could take to conditionally enable or disable tracing based on a flag received from your PHP backend:

    1. Use a Dynamic Flag: Instead of trying to disable OpenTelemetry, you can check the flag in each request handler and decide whether or not to create a new span for tracing. This is not true disabling but rather conditional tracing.

    2. Middleware to Act on the Flag: Implement middleware in your Express server that checks the tracing flag in the incoming request from the PHP server and conditionally creates spans based on this flag.

    3. Suppress Instrumentation: You can suppress or modify span generation dynamically, using a custom span processor that acts based on the request properties or global flags.

    Here’s how you could implement the above approach using Node.js and Express:

    const express = require('express');
    const { trace } = require('@opentelemetry/api');
    const { SimpleSpanProcessor } = require('@opentelemetry/tracing');
    
    // Assuming you have initialized OpenTelemetry and have a tracer available
    const tracer = trace.getTracer('express-server');
    
    // Custom span processor that can suppress span exporting
    class ConditionalExportSpanProcessor extends SimpleSpanProcessor {
      constructor(exporter, shouldTrace) {
        super(exporter);
        this.shouldTrace = shouldTrace;
      }
    
      // Override the `onEnd` method to conditionally export spans
      onEnd(span) {
        if (this.shouldTrace(span)) {
          super.onEnd(span);
        }
      }
      
      // Call this method to update the tracing condition
      updateShouldTrace(shouldTrace) {
        this.shouldTrace = shouldTrace;
      }
    }
    
    // A function to determine if a span should be exported
    function shouldTrace(span) {
      // Your logic to determine if tracing should be enabled
      // For example, based on a request flag
      return someCondition; // Replace with actual condition checking logic
    }
    
    // Initialize your span processor with the condition
    const spanProcessor = new ConditionalExportSpanProcessor(yourExporter, shouldTrace);
    tracer.addSpanProcessor(spanProcessor);
    
    const app = express();
    
    // Middleware to parse incoming requests as JSON
    app.use(express.json());
    
    // Middleware to check if tracing is enabled for a specific request
    app.use((req, res, next) => {
      // Assume the tracing flag is passed in a header or the request body
      const tracingEnabled = req.body.tracingEnabled || false;
    
      // Update the condition on which to decide tracing dynamically
      spanProcessor.updateShouldTrace(() => tracingEnabled);
    
      next();
    });
    
    // Define your routes as usual
    app.get('/', (req, res) => {
      // The span will only be created and exported if the condition is met
      const span = tracer.startSpan('your-operation-name');
      res.send('Hello, world!');
      span.end();
    });
    
    // Start your server
    app.listen(3000, () => {
      console.log('Server is running on port 3000');
    });
    

    In the above code, ConditionalExportSpanProcessor is a custom span processor that uses a dynamic function shouldTrace to determine whether or not to export a span. The middleware updates this condition based on the request’s tracingEnabled flag. The shouldTrace function will need to be defined by you and can include any logic you need to decide whether to trace or not.

    This strategy allows you to maintain the state of tracing throughout your application’s runtime while providing the flexibility to enable or disable it based on the needs of individual requests. It’s important to note that this method will only prevent the export of new spans created after the flag is received and won’t retroactively stop spans that have already been started from being exported.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search