TracerContext.java 7.8 KB
Newer Older
1
package org.skywalking.apm.agent.core.context;
P
pengys5 已提交
2

wu-sheng's avatar
wu-sheng 已提交
3 4
import java.util.LinkedList;
import java.util.List;
5
import org.skywalking.apm.agent.core.boot.ServiceManager;
wu-sheng's avatar
wu-sheng 已提交
6
import org.skywalking.apm.agent.core.conf.Config;
7
import org.skywalking.apm.agent.core.sampling.SamplingService;
P
pengys5 已提交
8 9 10
import org.skywalking.apm.trace.Span;
import org.skywalking.apm.trace.TraceSegment;
import org.skywalking.apm.trace.TraceSegmentRef;
11 12

/**
13
 * {@link TracerContext} maintains the context.
14
 * You manipulate (create/finish/get) spans and (inject/extract) context.
P
pengys5 已提交
15
 * <p>
16 17
 * Created by wusheng on 2017/2/17.
 */
18
public final class TracerContext {
19 20 21 22
    private TraceSegment segment;

    /**
     * Active spans stored in a Stack, usually called 'ActiveSpanStack'.
23
     * This {@link LinkedList} is the in-memory storage-structure.
P
pengys5 已提交
24
     * <p>
25
     * I use {@link LinkedList#removeLast()}, {@link LinkedList#addLast(Object)} and {@link LinkedList#last} instead of
26 27
     * {@link #pop()}, {@link #push(Span)}, {@link #peek()}
     */
28
    private LinkedList<Span> activeSpanStack = new LinkedList<Span>();
29 30 31

    private int spanIdGenerator;

32 33 34
    /**
     * Create a {@link TraceSegment} and init {@link #spanIdGenerator} as 0;
     */
35
    TracerContext() {
36
        this.segment = new TraceSegment(Config.Agent.APPLICATION_CODE);
37
        ServiceManager.INSTANCE.findService(SamplingService.class).trySampling(this.segment);
38 39 40 41 42 43 44 45 46
        this.spanIdGenerator = 0;
    }

    /**
     * Create a new span, as an active span, by the given operationName
     *
     * @param operationName {@link Span#operationName}
     * @return the new active span.
     */
47 48
    public Span createSpan(String operationName, boolean isLeaf) {
        return this.createSpan(operationName, System.currentTimeMillis(), isLeaf);
49 50 51 52 53 54
    }

    /**
     * Create a new span, as an active span, by the given operationName and startTime;
     *
     * @param operationName {@link Span#operationName}
55 56
     * @param startTime {@link Span#startTime}
     * @param isLeaf is true, if the span is a leaf in trace tree.
57 58
     * @return
     */
59
    public Span createSpan(String operationName, long startTime, boolean isLeaf) {
60 61 62
        Span parentSpan = peek();
        Span span;
        if (parentSpan == null) {
63 64 65 66 67 68 69 70 71 72
            if (isLeaf) {
                span = new LeafSpan(spanIdGenerator++, operationName, startTime);
            } else {
                span = new Span(spanIdGenerator++, operationName, startTime);
            }
            push(span);
        } else if (parentSpan.isLeaf()) {
            span = parentSpan;
            LeafSpan leafSpan = (LeafSpan)span;
            leafSpan.push();
73
        } else {
74 75 76 77 78 79
            if (isLeaf) {
                span = new LeafSpan(spanIdGenerator++, parentSpan, operationName, startTime);
            } else {
                span = new Span(spanIdGenerator++, parentSpan, operationName, startTime);
            }
            push(span);
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
        }
        return span;
    }

    /**
     * @return the active span of current context.
     */
    public Span activeSpan() {
        Span span = peek();
        if (span == null) {
            throw new IllegalStateException("No active span.");
        }
        return span;
    }

    /**
     * Stop the span. And finish the {@link #segment} if all {@link #activeSpanStack} elements are finished.
     *
     * @param span to finish. It must the the top element of {@link #activeSpanStack}.
     */
    public void stopSpan(Span span) {
101 102 103
        stopSpan(span, System.currentTimeMillis());
    }

104
    /**
105
     * @return the current trace id.
106
     */
107
    String getGlobalTraceId() {
108
        return segment.getRelatedGlobalTraces().get(0).get();
109 110
    }

111
    public void stopSpan(Span span, Long endTime) {
112
        Span lastSpan = peek();
113 114 115 116 117 118 119
        if (lastSpan.isLeaf()) {
            LeafSpan leafSpan = (LeafSpan)lastSpan;
            leafSpan.pop();
            if (!leafSpan.isFinished()) {
                return;
            }
        }
120
        if (lastSpan == span) {
121
            pop().finish(segment, endTime);
122 123 124 125 126
        } else {
            throw new IllegalStateException("Stopping the unexpected span = " + span);
        }

        if (activeSpanStack.isEmpty()) {
127
            this.finish();
128 129 130
        }
    }

wu-sheng's avatar
wu-sheng 已提交
131 132 133
    /**
     * Finish this context, and notify all {@link TracerContextListener}s, managed by {@link ListenerManager}
     */
134
    private void finish() {
wu-sheng's avatar
wu-sheng 已提交
135
        ListenerManager.notifyFinish(segment.finish());
136 137
    }

138
    /**
139
     * Give a snapshot of this {@link TracerContext},
140 141 142 143
     * and save current state to the given {@link ContextCarrier}.
     *
     * @param carrier holds the snapshot
     */
wu-sheng's avatar
wu-sheng 已提交
144
    public void inject(ContextCarrier carrier) {
145
        carrier.setTraceSegmentId(this.segment.getTraceSegmentId());
146 147
        Span span = this.activeSpan();
        carrier.setSpanId(span.getSpanId());
148
        carrier.setApplicationCode(Config.Agent.APPLICATION_CODE);
wu-sheng's avatar
wu-sheng 已提交
149
        String host = span.getPeerHost();
150
        if (host != null) {
wu-sheng's avatar
wu-sheng 已提交
151
            Integer port = span.getPort();
152
            carrier.setPeerHost(host + ":" + port);
153
        } else {
wu-sheng's avatar
wu-sheng 已提交
154
            carrier.setPeerHost(span.getPeers());
155
        }
156
        carrier.setDistributedTraceIds(this.segment.getRelatedGlobalTraces());
157
        carrier.setSampled(this.segment.isSampled());
158 159 160 161 162 163
    }

    /**
     * Ref this {@link ContextCarrier} to this {@link TraceSegment}
     *
     * @param carrier holds the snapshot, if get this {@link ContextCarrier} from remote, make sure {@link
164
     * ContextCarrier#deserialize(String)} called.
165
     */
166
    public void extract(ContextCarrier carrier) {
167
        if (carrier.isValid()) {
wu-sheng's avatar
wu-sheng 已提交
168
            this.segment.ref(getRef(carrier));
169
            ServiceManager.INSTANCE.findService(SamplingService.class).setSampleWhenExtract(this.segment, carrier);
170
            this.segment.relatedGlobalTraces(carrier.getDistributedTraceIds());
wu-sheng's avatar
wu-sheng 已提交
171 172 173
        }
    }

174
    private TraceSegmentRef getRef(ContextCarrier carrier) {
wu-sheng's avatar
wu-sheng 已提交
175 176 177
        TraceSegmentRef ref = new TraceSegmentRef();
        ref.setTraceSegmentId(carrier.getTraceSegmentId());
        ref.setSpanId(carrier.getSpanId());
178 179
        ref.setApplicationCode(carrier.getApplicationCode());
        ref.setPeerHost(carrier.getPeerHost());
wu-sheng's avatar
wu-sheng 已提交
180
        return ref;
181 182 183 184 185 186
    }

    /**
     * @return the top element of 'ActiveSpanStack', and remove it.
     */
    private Span pop() {
187
        return activeSpanStack.removeLast();
188 189 190 191 192 193 194 195
    }

    /**
     * Add a new Span at the top of 'ActiveSpanStack'
     *
     * @param span
     */
    private void push(Span span) {
196
        activeSpanStack.addLast(span);
197 198 199 200 201 202 203 204 205
    }

    /**
     * @return the top element of 'ActiveSpanStack' only.
     */
    private Span peek() {
        if (activeSpanStack.isEmpty()) {
            return null;
        }
206
        return activeSpanStack.getLast();
207
    }
208 209

    public static class ListenerManager {
210
        private static List<TracerContextListener> LISTENERS = new LinkedList<TracerContextListener>();
211 212

        /**
213
         * Add the given {@link TracerContextListener} to {@link #LISTENERS} list.
214 215 216 217
         *
         * @param listener the new listener.
         */
        public static synchronized void add(TracerContextListener listener) {
218
            LISTENERS.add(listener);
219 220 221 222
        }

        /**
         * Notify the {@link ListenerManager} about the given {@link TraceSegment} have finished.
223
         * And trigger {@link ListenerManager} to notify all {@link #LISTENERS} 's
224 225 226 227 228
         * {@link TracerContextListener#afterFinished(TraceSegment)}
         *
         * @param finishedSegment
         */
        static void notifyFinish(TraceSegment finishedSegment) {
229
            for (TracerContextListener listener : LISTENERS) {
230 231 232
                listener.afterFinished(finishedSegment);
            }
        }
wu-sheng's avatar
wu-sheng 已提交
233 234 235 236

        /**
         * Clear the given {@link TracerContextListener}
         */
237 238
        public static synchronized void remove(TracerContextListener listener) {
            LISTENERS.remove(listener);
wu-sheng's avatar
wu-sheng 已提交
239
        }
240
    }
241
}