All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.datadoghq.agent.OpenTracingHelper Maven / Gradle / Ivy

/*
 * Copyright 2017 Red Hat, Inc. and/or its affiliates
 * and other contributors as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.datadoghq.agent;

import io.opentracing.ActiveSpan;
import io.opentracing.BaseSpan;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.Tracer;
import io.opentracing.Tracer.SpanBuilder;
import io.opentracing.contrib.tracerresolver.TracerResolver;
import io.opentracing.propagation.Format;
import io.opentracing.util.GlobalTracer;
import java.net.HttpURLConnection;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.byteman.rule.Rule;
import org.jboss.byteman.rule.helper.Helper;

/** This class provides helper capabilities to the byteman rules. */
public class OpenTracingHelper extends Helper {

  private static final Logger log = Logger.getLogger(OpenTracingHelper.class.getName());

  private static Tracer tracer;

  private static final Map spanAssociations =
      Collections.synchronizedMap(new WeakHashMap());
  private static final Map finished =
      Collections.synchronizedMap(new WeakHashMap());

  private static final Map state =
      Collections.synchronizedMap(new WeakHashMap());

  private static final Object SYNC = new Object();

  public OpenTracingHelper(final Rule rule) {
    super(rule);
  }

  /**
   * This method returns the OpenTracing tracer.
   *
   * @return The tracer
   */
  public Tracer getTracer() {
    if (tracer == null) {
      // Initialize on first use
      initTracer();
    }
    return tracer;
  }

  protected void initTracer() {
    synchronized (SYNC) {
      if (tracer == null) {
        if (!GlobalTracer.isRegistered()) {
          // Try to obtain a tracer using the TracerResolver
          final Tracer resolved = TracerResolver.resolveTracer();
          if (resolved != null) {
            try {
              GlobalTracer.register(resolved);
            } catch (final RuntimeException re) {
              log.log(Level.WARNING, "Failed to register tracer '" + resolved + "'", re);
            }
          }
        }
        // Initialize the tracer even if one has not been registered
        // (i.e. it will use a NoopTracer under the covers)
        tracer = new AgentTracer(GlobalTracer.get());
      }
    }
  }

  /**
   * This method establishes an association between an application object (i.e. the subject of the
   * instrumentation) and a span. Once the application object is no longer being used, the
   * association with the span will automatically be discarded.
   *
   * @param obj The application object to be associated with the span
   * @param span The span
   */
  public void associateSpan(final Object obj, final Span span) {
    spanAssociations.put(obj, span);
  }

  /**
   * This method retrieves the span associated with the supplied application object.
   *
   * @param obj The application object
   * @return The span, or null if no associated span exists
   */
  public Span retrieveSpan(final Object obj) {
    return spanAssociations.get(obj);
  }

  /** ******************************************* */
  /** Needs to be replaced by span.isFinished() */
  public void finishedSpan(final Object key, final Span span) {
    finished.put(key, span);
  }

  public boolean isFinished(final Object key) {
    return finished.containsKey(key);
  }
  /** ******************************************* */

  /**
   * This method enables an instrumentation rule to record a 'state' number against an application
   * object. This can be used in situations where rules are only applicable in certain states. For
   * example, the simple case for rules responsible for installing filters would be states
   * representing NOT_INSTALLED and INSTALLED. This means that the filter would only be installed if
   * the application object (target of the instrumentation rule) was in the NOT_INSTALLED state.
   * However other more complex scenarios may be encountered where more than two states are
   * required.
   *
   * @param obj The application object
   * @param value The state value
   */
  public void setState(final Object obj, final int value) {
    state.put(obj, new Integer(value));
  }

  /**
   * This method retrieves the current 'state' number associated with the supplied application
   * object.
   *
   * @param obj The application object
   * @return The state, or 0 if no state currently exists
   */
  public int getState(final Object obj) {
    final Integer value = state.get(obj);
    return value == null ? 0 : value.intValue();
  }

  /**
   * This method determines whether the instrumentation point, associated with the supplied object,
   * should be ignored.
   *
   * @param obj The instrumentation target
   * @return Whether the instrumentation point should be ignored
   */
  public boolean ignore(final Object obj) {
    boolean ignore = false;

    if (obj instanceof HttpURLConnection) {
      final String value = ((HttpURLConnection) obj).getRequestProperty("opentracing.ignore");
      ignore = value != null && value.equalsIgnoreCase("true");
    }

    // TODO: If other technologies need to use this feature,
    // then create an Adapter to wrap the specifics of each
    // technology and provide access to their properties

    if (ignore && log.isLoggable(Level.FINEST)) {
      log.finest("Ignoring request because the property [opentracing.ignore] is present.");
    }

    return ignore;
  }

  /**
   * Proxy tracer used for one purpose - to enable the rules to define a ChildOf relationship
   * without being concerned whether the supplied Span is null. If the spec (and Tracer
   * implementations) are updated to indicate a null should be ignored, then this proxy can be
   * removed.
   */
  public static class AgentTracer implements Tracer {

    private final Tracer tracer;

    public AgentTracer(final Tracer tracer) {
      this.tracer = tracer;
    }

    @Override
    public SpanBuilder buildSpan(final String operation) {
      return new AgentSpanBuilder(tracer.buildSpan(operation));
    }

    @Override
    public  SpanContext extract(final Format format, final C carrier) {
      return tracer.extract(format, carrier);
    }

    @Override
    public  void inject(final SpanContext ctx, final Format format, final C carrier) {
      tracer.inject(ctx, format, carrier);
    }

    @Override
    public ActiveSpan activeSpan() {
      return tracer.activeSpan();
    }

    @Override
    public ActiveSpan makeActive(final Span span) {
      return tracer.makeActive(span);
    }
  }

  public static class AgentSpanBuilder implements SpanBuilder {

    private final SpanBuilder spanBuilder;

    public AgentSpanBuilder(final SpanBuilder spanBuilder) {
      this.spanBuilder = spanBuilder;
    }

    @Override
    public SpanBuilder addReference(final String type, final SpanContext ctx) {
      if (ctx != null) {
        spanBuilder.addReference(type, ctx);
      }
      return this;
    }

    @Override
    public SpanBuilder asChildOf(final SpanContext ctx) {
      if (ctx != null) {
        spanBuilder.asChildOf(ctx);
      }
      return this;
    }

    @Override
    public SpanBuilder asChildOf(final BaseSpan span) {
      if (span != null) {
        spanBuilder.asChildOf(span);
      }
      return this;
    }

    @Override
    public Span start() {
      return spanBuilder.start();
    }

    @Override
    public SpanBuilder withStartTimestamp(final long ts) {
      spanBuilder.withStartTimestamp(ts);
      return this;
    }

    @Override
    public SpanBuilder withTag(final String name, final String value) {
      spanBuilder.withTag(name, value);
      return this;
    }

    @Override
    public SpanBuilder withTag(final String name, final boolean value) {
      spanBuilder.withTag(name, value);
      return this;
    }

    @Override
    public SpanBuilder withTag(final String name, final Number value) {
      spanBuilder.withTag(name, value);
      return this;
    }

    @Override
    public SpanBuilder ignoreActiveSpan() {
      spanBuilder.ignoreActiveSpan();
      return this;
    }

    @Override
    public ActiveSpan startActive() {
      return spanBuilder.startActive();
    }

    @Override
    public Span startManual() {
      return spanBuilder.startManual();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy