/** * Copyright 2015-2017 The OpenZipkin Authors * * 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 zipkin.internal; import zipkin.Annotation; import zipkin.BinaryAnnotation; import zipkin.Constants; import zipkin.Span; import static zipkin.internal.Util.checkNotNull; import static zipkin.internal.Util.equal; /** * Internal type used by {@link DependencyLinker linker} that holds the minimum state needed to * aggregate {@link zipkin.DependencyLink dependency links}. */ // fields not exposed as public to further discourage use as a general type public final class DependencyLinkSpan { /** Unique 8 or 16-byte identifier for a trace, set on all spans within it. */ static final class TraceId { /** 0 may imply 8-byte identifiers are in use */ final long hi; final long lo; TraceId(long hi, long lo) { this.hi = hi; this.lo = lo; } @Override public boolean equals(Object o) { if (o == this) return true; if (o instanceof TraceId) { TraceId that = (TraceId) o; return (this.hi == that.hi) && (this.lo == that.lo); } return false; } @Override public int hashCode() { int h = 1; h *= 1000003; h ^= (hi >>> 32) ^ hi; h *= 1000003; h ^= (lo >>> 32) ^ lo; return h; } } /** * Indicates the primary span type. */ enum Kind { CLIENT, /** The span includes a {@link zipkin.Constants#SERVER_RECV}. */ SERVER, UNKNOWN } final TraceId traceId; @Nullable final Long parentId; final long id; final Kind kind; @Nullable final String service; @Nullable final String peerService; DependencyLinkSpan(TraceId traceId, Long parentId, long id, Kind kind, String service, String peerService) { this.traceId = traceId; this.parentId = parentId; this.id = id; this.kind = checkNotNull(kind, "kind"); this.service = service; this.peerService = peerService; } @Override public String toString() { StringBuilder json = new StringBuilder(); json.append("{\"traceId\": \"").append(Util.toLowerHex(traceId.hi, traceId.lo)).append('\"'); if (parentId != null) { json.append(", \"parentId\": \"").append(Util.toLowerHex(parentId)).append('\"'); } json.append(", \"id\": \"").append(Util.toLowerHex(id)).append('\"'); json.append(", \"kind\": \"").append(kind).append('\"'); if (service != null) json.append(", \"service\": \"").append(service).append('\"'); if (peerService != null) json.append(", \"peerService\": \"").append(peerService).append('\"'); return json.append("}").toString(); } /** Only considers ID fields, as these spans are not expected to repeat */ @Override public boolean equals(Object o) { if (o == this) return true; if (o instanceof DependencyLinkSpan) { DependencyLinkSpan that = (DependencyLinkSpan) o; return equal(this.traceId, that.traceId) && equal(this.parentId, that.parentId) && (this.id == that.id); } return false; } /** Only considers ID fields, as these spans are not expected to repeat */ @Override public int hashCode() { int h = 1; h *= 1000003; h ^= traceId.hashCode(); h *= 1000003; h ^= (parentId == null) ? 0 : parentId.hashCode(); h *= 1000003; h ^= (id >>> 32) ^ id; return h; } public static Builder builder(long traceIdHigh, long traceIdLow, Long parentId, long spanId) { return new Builder(new TraceId(traceIdHigh, traceIdLow), parentId, spanId); } public static DependencyLinkSpan from(Span s) { TraceId traceId = new TraceId(s.traceIdHigh, s.traceId); DependencyLinkSpan.Builder linkSpan = new DependencyLinkSpan.Builder(traceId, s.parentId, s.id); for (BinaryAnnotation a : s.binaryAnnotations) { if (a.key.equals(Constants.CLIENT_ADDR) && a.endpoint != null) { linkSpan.caService(a.endpoint.serviceName); } else if (a.key.equals(Constants.SERVER_ADDR) && a.endpoint != null) { linkSpan.saService(a.endpoint.serviceName); } } for (Annotation a : s.annotations) { if (a.value.equals(Constants.SERVER_RECV) && a.endpoint != null) { linkSpan.srService(a.endpoint.serviceName); } else if (a.value.equals(Constants.CLIENT_SEND) && a.endpoint != null) { linkSpan.csService(a.endpoint.serviceName); } } return linkSpan.build(); } public static final class Builder { final TraceId traceId; final Long parentId; final long spanId; String srService; String csService; String caService; String saService; Builder(TraceId traceId, Long parentId, long spanId) { this.traceId = traceId; this.spanId = spanId; this.parentId = parentId; } /** * {@link zipkin.Constants#SERVER_RECV} is the preferred name of server, and this is a * traditional span. */ public Builder srService(String srService) { if ("".equals(srService)) srService = null; this.srService = srService; return this; } /** * {@link zipkin.Constants#CLIENT_SEND} is read to see calls into the root span from * instrumented clients. */ public Builder csService(String csService) { if ("".equals(csService)) csService = null; this.csService = csService; return this; } /** * {@link zipkin.Constants#CLIENT_ADDR} is read to see calls into the root span from * uninstrumented clients. */ public Builder caService(String caService) { if ("".equals(caService)) caService = null; this.caService = caService; return this; } /** * {@link zipkin.Constants#SERVER_ADDR} is only read at the leaf, when a client calls an * un-instrumented server. */ public Builder saService(String saService) { if ("".equals(saService)) saService = null; this.saService = saService; return this; } public DependencyLinkSpan build() { // Finagle labels two sides of the same socket ("ca", "sa") with the same name. // Skip the client side, so it isn't mistaken for a loopback request if (equal(saService, caService)) caService = null; if (srService != null) { // The client address is more authoritative than the client send owner. if (caService == null) caService = csService; return new DependencyLinkSpan(traceId, parentId, spanId, Kind.SERVER, srService, caService); } else if (saService != null) { return new DependencyLinkSpan(traceId, parentId, spanId, Kind.CLIENT, caService, saService); } return new DependencyLinkSpan(traceId, parentId, spanId, Kind.UNKNOWN, null, null); } } }