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

com.lightstep.tracer.shared.Span Maven / Gradle / Ivy

There is a newer version: 0.32.0
Show newest version
package com.lightstep.tracer.shared;

import com.lightstep.tracer.grpc.KeyValue;
import com.lightstep.tracer.grpc.Log;
import com.lightstep.tracer.grpc.Span.Builder;

import java.util.HashMap;
import java.util.Map;

public class Span implements io.opentracing.Span {

    static final String LOG_KEY_EVENT = "event";
    static final String LOG_KEY_MESSAGE = "message";

    private final Object mutex = new Object();
    private final AbstractTracer tracer;
    private final long startTimestampRelativeNanos;
    private final Builder grpcSpan;

    private SpanContext context;

    Span(AbstractTracer tracer, SpanContext context, Builder grpcSpan, long startTimestampRelativeNanos) {
        this.context = context;
        this.tracer = tracer;
        this.grpcSpan = grpcSpan;
        this.startTimestampRelativeNanos = startTimestampRelativeNanos;

        if (tracer != null && tracer.metaEventLoggingEnabled && Util.IsNotMetaSpan(this)) {
            tracer.buildSpan(LightStepConstants.MetaEvents.SpanStartOperation)
                    .ignoreActiveSpan()
                    .withTag(LightStepConstants.MetaEvents.MetaEventKey, true)
                    .withTag(LightStepConstants.MetaEvents.SpanIdKey, context.getSpanId())
                    .withTag(LightStepConstants.MetaEvents.TraceIdKey, context.getTraceId())
                    .start()
                    .finish();
        }
    }

    @Override
    public SpanContext context() {
        return context;
    }

    @Override
    public void finish() {
        finish(nowMicros());
    }

    @Override
    public void finish(long finishTimeMicros) {
        if (tracer.metaEventLoggingEnabled && Util.IsNotMetaSpan(this)) {
            tracer.buildSpan(LightStepConstants.MetaEvents.SpanFinishOperation)
                    .ignoreActiveSpan()
                    .withTag(LightStepConstants.MetaEvents.MetaEventKey, true)
                    .withTag(LightStepConstants.MetaEvents.SpanIdKey, context.getSpanId())
                    .withTag(LightStepConstants.MetaEvents.TraceIdKey, context.getTraceId())
                    .start()
                    .finish();
        }
        synchronized (mutex) {
            grpcSpan.setDurationMicros(durationMicros(finishTimeMicros));
            tracer.addSpan(grpcSpan.build());
        }
    }

    @Override
    public Span setTag(String key, String value) {
        if (key == null || value == null) {
            tracer.debug("key (" + key + ") or value (" + value + ") is null, ignoring");
            return this;
        }
        synchronized (mutex) {
            grpcSpan.addTags(KeyValue.newBuilder().setKey(key).setStringValue(value));
        }
        return this;
    }

    @Override
    public Span setTag(String key, boolean value) {
        if (key == null) {
            tracer.debug("key is null, ignoring");
            return this;
        }
        synchronized (mutex) {
            grpcSpan.addTags(KeyValue.newBuilder().setKey(key).setBoolValue(value));
        }
        return this;
    }

    @Override
    public Span setTag(String key, Number value) {
        if (key == null || value == null) {
            tracer.debug("key (" + key + ") or value (" + value + ") is null, ignoring");
            return this;
        }
        synchronized (mutex) {
            if (value instanceof Long || value instanceof Integer) {
                grpcSpan.addTags(KeyValue.newBuilder().setKey(key).setIntValue(value.longValue()));
            } else if (value instanceof Double || value instanceof Float) {
                grpcSpan
                    .addTags(KeyValue.newBuilder().setKey(key).setDoubleValue(value.doubleValue()));
            } else {
                grpcSpan
                    .addTags(KeyValue.newBuilder().setKey(key).setStringValue(value.toString()));
            }
        }
        return this;
    }

    @Override
    public  Span setTag(io.opentracing.tag.Tag tag, T value) {
        if (tag == null || value == null) {
            tracer.debug("tag (" + tag + ") or value (" + value + ") is null, ignoring");
            return this;
        }
        // No lock needed, as Tag ought to invoke one of the other setTag() overloads.
        tag.set(this, value);
        return this;
    }

    @Override
    public synchronized String getBaggageItem(String key) {
        return context.getBaggageItem(key);
    }

    @Override
    public synchronized Span setBaggageItem(String key, String value) {
        context = context.withBaggageItem(key, value);
        return this;
    }

    public synchronized Span setOperationName(String operationName) {
        grpcSpan.setOperationName(operationName);
        return this;
    }

    public Span setComponentName(String componentName) {
        if (componentName == null) {
            tracer.debug("componentName is null, ignoring");
            return this;
        }
        return setTag(LightStepConstants.Tags.COMPONENT_NAME_KEY, componentName);
    }

    @SuppressWarnings("WeakerAccess")
    public void close() {
        finish();
    }

    public AbstractTracer getTracer() {
        return tracer;
    }

    public final Span log(Map fields) {
        return log(nowMicros(), fields);
    }

    @Override
    public final Span log(long timestampMicros, Map fields) {
        com.lightstep.tracer.grpc.Log.Builder log = Log.newBuilder()
            .setTimestamp(Util.epochTimeMicrosToProtoTime(timestampMicros));
        for (Map.Entry kv : fields.entrySet()) {
            String key = kv.getKey();
            Object value = kv.getValue();

            if (key == null) {
                continue; // There's not much we can do here.
            }
            if (value == null) {
                value = ""; // Fallback
            }

            final KeyValue.Builder outKV = KeyValue.newBuilder().setKey(key);
            final Object inValue = value;

            if (inValue instanceof String) {
                outKV.setStringValue((String)inValue);
            } else if (inValue instanceof Number) {
                if (inValue instanceof Long || inValue instanceof Integer) {
                    outKV.setIntValue(((Number) inValue).longValue());
                } else if (inValue instanceof Double || inValue instanceof Float) {
                    outKV.setDoubleValue(((Number) inValue).doubleValue());
                } else {
                    outKV.setStringValue(inValue.toString());
                }
            } else if (inValue instanceof Boolean) {
                outKV.setBoolValue((Boolean)inValue);
            } else {
                outKV.setJsonValue(Span.stringToJSONValue(inValue.toString()));
            }
            log.addFields(outKV.build());
        }

        synchronized (mutex) {
            grpcSpan.addLogs(log.build());
        }
        return this;
    }

    @Override
    public Span log(String message) {
        return log(nowMicros(), message, null);
    }

    @Override
    public Span log(long timestampMicroseconds, String message) {
        return log(timestampMicroseconds, message, null);
    }

    private Span log(long timestampMicroseconds, String message, /* @Nullable */ Object payload) {
        Map fields = new HashMap<>();
        fields.put("message", message);
        if (payload != null) {
            fields.put("payload", payload);
        }
        return log(timestampMicroseconds, fields);
    }

    @SuppressWarnings("WeakerAccess")
    public String generateTraceURL() {
        return tracer.generateTraceURL(context.getSpanId());
    }

    private long nowMicros() {
        // Note that startTimestampRelativeNanos will be -1 if the user
        // provided an explicit start timestamp in the SpanBuilder.
        if (startTimestampRelativeNanos > 0) {
            long durationMicros = (System.nanoTime() - startTimestampRelativeNanos) / 1000;
            return Util.protoTimeToEpochMicros(grpcSpan.getStartTimestamp())+ durationMicros;
        } else {
            return System.currentTimeMillis() * 1000;
        }
    }

    private long durationMicros(long finishTimeMicros) {
        return finishTimeMicros - Util.protoTimeToEpochMicros(grpcSpan.getStartTimestamp());
    }

    /**
     * Quotes a plain string into a valid JSON value.
     *
     * Adapted from https://android.googlesource.com/platform/dalvik/libcore/json/src/main/java/org/json/JSONStringer.java
     */
    static String stringToJSONValue(String value) {
        StringBuilder out = new StringBuilder(value.length() + 2);
        out.append("\"");
        for (int i = 0, length = value.length(); i < length; i++) {
            char c = value.charAt(i);
            /*
             * From RFC 4627, "All Unicode characters may be placed within the
             * quotation marks except for the characters that must be escaped:
             * quotation mark, reverse solidus, and the control characters
             * (U+0000 through U+001F)."
             */
            switch (c) {
                case '"':
                case '\\':
                case '/':
                    out.append('\\').append(c);
                    break;
                case '\t':
                    out.append("\\t");
                    break;
                case '\b':
                    out.append("\\b");
                    break;
                case '\n':
                    out.append("\\n");
                    break;
                case '\r':
                    out.append("\\r");
                    break;
                case '\f':
                    out.append("\\f");
                    break;
                default:
                    if (c <= 0x1F) {
                        out.append(String.format("\\u%04x", (int) c));
                    } else {
                        out.append(c);
                    }
                    break;
            }
        }
        out.append("\"");
        return out.toString();
    }

    /**
     * For unit testing only.
     */
    long getStartTimestampRelativeNanos() {
        return startTimestampRelativeNanos;
    }

    /**
     * For unit testing in JRE test.
     */
    @SuppressWarnings("WeakerAccess")
    public Builder getGrpcSpan() {
        return grpcSpan;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy