commit bb3290967ed85b4a7f6990c354476bf583dce01f Author: stack Date: Sat Sep 19 20:25:23 2015 -0700 Rename htrace-core as htrace-core4 diff --git a/htrace-core/pom.xml b/htrace-core/pom.xml deleted file mode 100644 index 81f2f87..0000000 --- a/htrace-core/pom.xml +++ /dev/null @@ -1,125 +0,0 @@ - - - - 4.0.0 - - htrace-core4 - jar - - - htrace - org.apache.htrace - 4.1.0-incubating-SNAPSHOT - .. - - - htrace-core - http://incubator.apache.org/projects/htrace.html - - - UTF-8 - - - - - - org.apache.maven.plugins - maven-source-plugin - - - maven-javadoc-plugin - - - maven-compiler-plugin - - - org.apache.maven.plugins - maven-shade-plugin - - - package - - - - org.apache.commons.logging - org.apache.htrace.commons.logging - - - com.fasterxml.jackson - org.apache.htrace.fasterxml.jackson - - - - - shade - - - - - - org.apache.maven.plugins - maven-gpg-plugin - - - org.apache.maven.plugins - maven-jar-plugin - - - org.apache.rat - apache-rat-plugin - - - - maven-deploy-plugin - - - - - - - - junit - junit - test - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-databind - - - - commons-logging - commons-logging - - - - - - dist - - - - - maven-assembly-plugin - - true - - - - - - - diff --git a/htrace-core/src/main/java/org/apache/htrace/core/AlwaysSampler.java b/htrace-core/src/main/java/org/apache/htrace/core/AlwaysSampler.java deleted file mode 100644 index 8d5a296..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/AlwaysSampler.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -/** - * A Sampler that always returns true. - */ -public final class AlwaysSampler extends Sampler { - public static final AlwaysSampler INSTANCE = new AlwaysSampler(null); - - public AlwaysSampler(HTraceConfiguration conf) { - } - - @Override - public boolean next() { - return true; - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/CountSampler.java b/htrace-core/src/main/java/org/apache/htrace/core/CountSampler.java deleted file mode 100644 index 5a838c7..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/CountSampler.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.util.concurrent.ThreadLocalRandom; - -/** - * Sampler that returns true every N calls. Specify the frequency interval by configuring a - * {@code long} value for {@link #SAMPLER_FREQUENCY_CONF_KEY}. - */ -public class CountSampler extends Sampler { - public final static String SAMPLER_FREQUENCY_CONF_KEY = "sampler.frequency"; - - final long frequency; - long count = ThreadLocalRandom.current().nextLong(); - - public CountSampler(HTraceConfiguration conf) { - this.frequency = Long.parseLong(conf.get(SAMPLER_FREQUENCY_CONF_KEY), 10); - } - - @Override - public boolean next() { - return (count++ % frequency) == 0; - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/HTraceConfiguration.java b/htrace-core/src/main/java/org/apache/htrace/core/HTraceConfiguration.java deleted file mode 100644 index c6e445b..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/HTraceConfiguration.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.util.HashMap; -import java.util.Map; - -/** - * Wrapper which integrating applications should implement in order - * to provide tracing configuration. - */ -public abstract class HTraceConfiguration { - - private static final Log LOG = LogFactory.getLog(HTraceConfiguration.class); - - private static final Map EMPTY_MAP = new HashMap(1); - - /** - * An empty HTrace configuration. - */ - public static final HTraceConfiguration EMPTY = fromMap(EMPTY_MAP); - - /** - * Create an HTrace configuration from a map. - * - * @param conf The map to create the configuration from. - * @return The new configuration. - */ - public static HTraceConfiguration fromMap(Map conf) { - return new MapConf(conf); - } - - public static HTraceConfiguration fromKeyValuePairs(String... pairs) { - if ((pairs.length % 2) != 0) { - throw new RuntimeException("You must specify an equal number of keys " + - "and values."); - } - Map conf = new HashMap(); - for (int i = 0; i < pairs.length; i+=2) { - conf.put(pairs[i], pairs[i + 1]); - } - return new MapConf(conf); - } - - public abstract String get(String key); - - public abstract String get(String key, String defaultValue); - - public boolean getBoolean(String key, boolean defaultValue) { - String value = get(key, String.valueOf(defaultValue)).trim().toLowerCase(); - - if ("true".equals(value)) { - return true; - } else if ("false".equals(value)) { - return false; - } - - LOG.warn("Expected boolean for key [" + key + "] instead got [" + value + "]."); - return defaultValue; - } - - public int getInt(String key, int defaultVal) { - String val = get(key); - if (val == null || val.trim().isEmpty()) { - return defaultVal; - } - try { - return Integer.parseInt(val); - } catch (NumberFormatException nfe) { - throw new IllegalArgumentException("Bad value for '" + key + "': should be int"); - } - } - - private static class MapConf extends HTraceConfiguration { - private final Map conf; - - public MapConf(Map conf) { - this.conf = new HashMap(conf); - } - - @Override - public String get(String key) { - return conf.get(key); - } - - @Override - public String get(String key, String defaultValue) { - String value = get(key); - return value == null ? defaultValue : value; - } - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java deleted file mode 100644 index 69a43b1..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.file.FileSystems; -import java.nio.file.StandardOpenOption; -import java.util.UUID; -import java.util.concurrent.locks.ReentrantLock; - -/** - * Writes the spans it receives to a local file. - */ -public class LocalFileSpanReceiver extends SpanReceiver { - private static final Log LOG = LogFactory.getLog(LocalFileSpanReceiver.class); - public static final String PATH_KEY = "local.file.span.receiver.path"; - public static final String CAPACITY_KEY = "local.file.span.receiver.capacity"; - public static final int CAPACITY_DEFAULT = 5000; - private static ObjectWriter JSON_WRITER = new ObjectMapper().writer(); - private final String path; - - private byte[][] bufferedSpans; - private int bufferedSpansIndex; - private final ReentrantLock bufferLock = new ReentrantLock(); - - private final FileOutputStream stream; - private final FileChannel channel; - private final ReentrantLock channelLock = new ReentrantLock(); - - public LocalFileSpanReceiver(HTraceConfiguration conf) { - int capacity = conf.getInt(CAPACITY_KEY, CAPACITY_DEFAULT); - if (capacity < 1) { - throw new IllegalArgumentException(CAPACITY_KEY + " must not be " + - "less than 1."); - } - String pathStr = conf.get(PATH_KEY); - if (pathStr == null || pathStr.isEmpty()) { - path = getUniqueLocalTraceFileName(); - } else { - path = pathStr; - } - boolean success = false; - try { - this.stream = new FileOutputStream(path, true); - } catch (IOException ioe) { - LOG.error("Error opening " + path + ": " + ioe.getMessage()); - throw new RuntimeException(ioe); - } - this.channel = stream.getChannel(); - if (this.channel == null) { - try { - this.stream.close(); - } catch (IOException e) { - LOG.error("Error closing " + path, e); - } - LOG.error("Failed to get channel for " + path); - throw new RuntimeException("Failed to get channel for " + path); - } - this.bufferedSpans = new byte[capacity][]; - this.bufferedSpansIndex = 0; - if (LOG.isDebugEnabled()) { - LOG.debug("Created new LocalFileSpanReceiver with path = " + path + - ", capacity = " + capacity); - } - } - - /** - * Number of buffers to use in FileChannel#write. - * - * On UNIX, FileChannel#write uses writev-- a kernel interface that allows - * us to send multiple buffers at once. This is more efficient than making a - * separate write call for each buffer, since it minimizes the number of - * transitions from userspace to kernel space. - */ - private final int WRITEV_SIZE = 20; - - private final static ByteBuffer newlineBuf = - ByteBuffer.wrap(new byte[] { (byte)0xa }); - - /** - * Flushes a bufferedSpans array. - */ - private void doFlush(byte[][] toFlush, int len) throws IOException { - int bidx = 0, widx = 0; - ByteBuffer writevBufs[] = new ByteBuffer[2 * WRITEV_SIZE]; - - while (true) { - if (widx == writevBufs.length) { - channel.write(writevBufs); - widx = 0; - } - if (bidx == len) { - break; - } - writevBufs[widx] = ByteBuffer.wrap(toFlush[bidx]); - writevBufs[widx + 1] = newlineBuf; - bidx++; - widx+=2; - } - if (widx > 0) { - channel.write(writevBufs, 0, widx); - } - } - - @Override - public void receiveSpan(Span span) { - // Serialize the span data into a byte[]. Note that we're not holding the - // lock here, to improve concurrency. - byte jsonBuf[] = null; - try { - jsonBuf = JSON_WRITER.writeValueAsBytes(span); - } catch (JsonProcessingException e) { - LOG.error("receiveSpan(path=" + path + ", span=" + span + "): " + - "Json processing error: " + e.getMessage()); - return; - } - - // Grab the bufferLock and put our jsonBuf into the list of buffers to - // flush. - byte toFlush[][] = null; - bufferLock.lock(); - try { - if (bufferedSpans == null) { - LOG.debug("receiveSpan(path=" + path + ", span=" + span + "): " + - "LocalFileSpanReceiver for " + path + " is closed."); - return; - } - bufferedSpans[bufferedSpansIndex] = jsonBuf; - bufferedSpansIndex++; - if (bufferedSpansIndex == bufferedSpans.length) { - // If we've hit the limit for the number of buffers to flush, - // swap out the existing bufferedSpans array for a new array, and - // prepare to flush those spans to disk. - toFlush = bufferedSpans; - bufferedSpansIndex = 0; - bufferedSpans = new byte[bufferedSpans.length][]; - } - } finally { - bufferLock.unlock(); - } - if (toFlush != null) { - // We released the bufferLock above, to avoid blocking concurrent - // receiveSpan calls. But now, we must take the channelLock, to make - // sure that we have sole access to the output channel. If we did not do - // this, we might get interleaved output. - // - // There is a small chance that another thread doing a flush of more - // recent spans could get ahead of us here, and take the lock before we - // do. This is ok, since spans don't have to be written out in order. - channelLock.lock(); - try { - doFlush(toFlush, toFlush.length); - } catch (IOException ioe) { - LOG.error("Error flushing buffers to " + path + ": " + - ioe.getMessage()); - } finally { - channelLock.unlock(); - } - } - } - - @Override - public void close() throws IOException { - byte toFlush[][] = null; - int numToFlush = 0; - bufferLock.lock(); - try { - if (bufferedSpans == null) { - LOG.info("LocalFileSpanReceiver for " + path + " was already closed."); - return; - } - numToFlush = bufferedSpansIndex; - bufferedSpansIndex = 0; - toFlush = bufferedSpans; - bufferedSpans = null; - } finally { - bufferLock.unlock(); - } - channelLock.lock(); - try { - doFlush(toFlush, numToFlush); - } catch (IOException ioe) { - LOG.error("Error flushing buffers to " + path + ": " + - ioe.getMessage()); - } finally { - try { - stream.close(); - } catch (IOException e) { - LOG.error("Error closing stream for " + path, e); - } - channelLock.unlock(); - } - } - - public static String getUniqueLocalTraceFileName() { - String tmp = System.getProperty("java.io.tmpdir", "/tmp"); - String nonce = null; - BufferedReader reader = null; - try { - // On Linux we can get a unique local file name by reading the process id - // out of /proc/self/stat. (There isn't any portable way to get the - // process ID from Java.) - reader = new BufferedReader( - new InputStreamReader(new FileInputStream("/proc/self/stat"), - "UTF-8")); - String line = reader.readLine(); - if (line == null) { - throw new EOFException(); - } - nonce = line.split(" ")[0]; - } catch (IOException e) { - } finally { - if (reader != null) { - try { - reader.close(); - } catch(IOException e) { - LOG.warn("Exception in closing " + reader, e); - } - } - } - if (nonce == null) { - // If we can't use the process ID, use a random nonce. - nonce = UUID.randomUUID().toString(); - } - return new File(tmp, nonce).getAbsolutePath(); - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/MilliSpan.java b/htrace-core/src/main/java/org/apache/htrace/core/MilliSpan.java deleted file mode 100644 index 5dd6bdb..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/MilliSpan.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectReader; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * A Span implementation that stores its information in milliseconds since the - * epoch. - */ -@JsonDeserialize(using = MilliSpan.MilliSpanDeserializer.class) -public class MilliSpan implements Span { - private static ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private static ObjectReader JSON_READER = OBJECT_MAPPER.reader(MilliSpan.class); - private static ObjectWriter JSON_WRITER = OBJECT_MAPPER.writer(); - private static final SpanId EMPTY_PARENT_ARRAY[] = new SpanId[0]; - private static final String EMPTY_STRING = ""; - - private long begin; - private long end; - private final String description; - private SpanId parents[]; - private final SpanId spanId; - private Map traceInfo = null; - private String tracerId; - private List timeline = null; - - @Override - public Span child(String childDescription) { - return new MilliSpan.Builder(). - begin(System.currentTimeMillis()). - end(0). - description(childDescription). - parents(new SpanId[] {spanId}). - spanId(spanId.newChildId()). - tracerId(tracerId). - build(); - } - - /** - * The public interface for constructing a MilliSpan. - */ - public static class Builder { - private long begin; - private long end; - private String description = EMPTY_STRING; - private SpanId parents[] = EMPTY_PARENT_ARRAY; - private SpanId spanId = SpanId.INVALID; - private Map traceInfo = null; - private String tracerId = EMPTY_STRING; - private List timeline = null; - - public Builder() { - } - - public Builder begin(long begin) { - this.begin = begin; - return this; - } - - public Builder end(long end) { - this.end = end; - return this; - } - - public Builder description(String description) { - this.description = description; - return this; - } - - public Builder parents(SpanId parents[]) { - this.parents = parents; - return this; - } - - public Builder parents(List parentList) { - SpanId[] parents = new SpanId[parentList.size()]; - for (int i = 0; i < parentList.size(); i++) { - parents[i] = parentList.get(i); - } - this.parents = parents; - return this; - } - - public Builder spanId(SpanId spanId) { - this.spanId = spanId; - return this; - } - - public Builder traceInfo(Map traceInfo) { - this.traceInfo = traceInfo.isEmpty() ? null : traceInfo; - return this; - } - - public Builder tracerId(String tracerId) { - this.tracerId = tracerId; - return this; - } - - public Builder timeline(List timeline) { - this.timeline = timeline.isEmpty() ? null : timeline; - return this; - } - - public MilliSpan build() { - return new MilliSpan(this); - } - } - - public MilliSpan() { - this.begin = 0; - this.end = 0; - this.description = EMPTY_STRING; - this.parents = EMPTY_PARENT_ARRAY; - this.spanId = SpanId.INVALID; - this.traceInfo = null; - this.tracerId = EMPTY_STRING; - this.timeline = null; - } - - private MilliSpan(Builder builder) { - this.begin = builder.begin; - this.end = builder.end; - this.description = builder.description; - this.parents = builder.parents; - this.spanId = builder.spanId; - this.traceInfo = builder.traceInfo; - this.tracerId = builder.tracerId; - this.timeline = builder.timeline; - } - - @Override - public synchronized void stop() { - if (end == 0) { - if (begin == 0) - throw new IllegalStateException("Span for " + description - + " has not been started"); - end = System.currentTimeMillis(); - } - } - - protected long currentTimeMillis() { - return System.currentTimeMillis(); - } - - @Override - public synchronized boolean isRunning() { - return begin != 0 && end == 0; - } - - @Override - public synchronized long getAccumulatedMillis() { - if (begin == 0) - return 0; - if (end > 0) - return end - begin; - return currentTimeMillis() - begin; - } - - @Override - public String toString() { - return toJson(); - } - - @Override - public String getDescription() { - return description; - } - - @Override - public SpanId getSpanId() { - return spanId; - } - - @Override - public SpanId[] getParents() { - return parents; - } - - @Override - public void setParents(SpanId[] parents) { - this.parents = parents; - } - - @Override - public long getStartTimeMillis() { - return begin; - } - - @Override - public long getStopTimeMillis() { - return end; - } - - @Override - public void addKVAnnotation(String key, String value) { - if (traceInfo == null) - traceInfo = new HashMap(); - traceInfo.put(key, value); - } - - @Override - public void addTimelineAnnotation(String msg) { - if (timeline == null) { - timeline = new ArrayList(); - } - timeline.add(new TimelineAnnotation(System.currentTimeMillis(), msg)); - } - - @Override - public Map getKVAnnotations() { - if (traceInfo == null) - return Collections.emptyMap(); - return Collections.unmodifiableMap(traceInfo); - } - - @Override - public List getTimelineAnnotations() { - if (timeline == null) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(timeline); - } - - @Override - public String getTracerId() { - return tracerId; - } - - @Override - public void setTracerId(String tracerId) { - this.tracerId = tracerId; - } - - @Override - public String toJson() { - StringWriter writer = new StringWriter(); - try { - JSON_WRITER.writeValue(writer, this); - } catch (IOException e) { - // An IOException should not be possible when writing to a string. - throw new RuntimeException(e); - } - return writer.toString(); - } - - public static class MilliSpanDeserializer - extends JsonDeserializer { - @Override - public MilliSpan deserialize(JsonParser jp, DeserializationContext ctxt) - throws IOException, JsonProcessingException { - JsonNode root = jp.getCodec().readTree(jp); - Builder builder = new Builder(); - JsonNode bNode = root.get("b"); - if (bNode != null) { - builder.begin(bNode.asLong()); - } - JsonNode eNode = root.get("e"); - if (eNode != null) { - builder.end(eNode.asLong()); - } - JsonNode dNode = root.get("d"); - if (dNode != null) { - builder.description(dNode.asText()); - } - JsonNode sNode = root.get("a"); - if (sNode != null) { - builder.spanId(SpanId.fromString(sNode.asText())); - } - JsonNode rNode = root.get("r"); - if (rNode != null) { - builder.tracerId(rNode.asText()); - } - JsonNode parentsNode = root.get("p"); - LinkedList parents = new LinkedList(); - if (parentsNode != null) { - for (Iterator iter = parentsNode.elements(); - iter.hasNext(); ) { - JsonNode parentIdNode = iter.next(); - parents.add(SpanId.fromString(parentIdNode.asText())); - } - } - builder.parents(parents); - JsonNode traceInfoNode = root.get("n"); - if (traceInfoNode != null) { - HashMap traceInfo = new HashMap(); - for (Iterator iter = traceInfoNode.fieldNames(); - iter.hasNext(); ) { - String field = iter.next(); - traceInfo.put(field, traceInfoNode.get(field).asText()); - } - builder.traceInfo(traceInfo); - } - JsonNode timelineNode = root.get("t"); - if (timelineNode != null) { - LinkedList timeline = - new LinkedList(); - for (Iterator iter = timelineNode.elements(); - iter.hasNext(); ) { - JsonNode ann = iter.next(); - timeline.add(new TimelineAnnotation(ann.get("t").asLong(), - ann.get("m").asText())); - } - builder.timeline(timeline); - } - return builder.build(); - } - } - - public static MilliSpan fromJson(String json) throws IOException { - return JSON_READER.readValue(json); - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/NeverSampler.java b/htrace-core/src/main/java/org/apache/htrace/core/NeverSampler.java deleted file mode 100644 index 60cc7d2..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/NeverSampler.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -/** - * A Sampler that never returns true. - */ -public final class NeverSampler extends Sampler { - public static final NeverSampler INSTANCE = new NeverSampler(null); - - public NeverSampler(HTraceConfiguration conf) { - } - - @Override - public boolean next() { - return false; - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/NullScope.java b/htrace-core/src/main/java/org/apache/htrace/core/NullScope.java deleted file mode 100644 index fe76e46..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/NullScope.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -/** - * An empty {@link TraceScope}. - */ -class NullScope extends TraceScope { - NullScope(Tracer tracer) { - super(tracer, null, null); - } - - @Override - public SpanId getSpanId() { - return SpanId.INVALID; - } - - @Override - public void detach() { - if (detached) { - Tracer.throwClientError("Can't detach this TraceScope because " + - "it is already detached."); - } - detached = true; - } - - @Override - public void reattach() { - if (!detached) { - Tracer.throwClientError("Can't reattach this TraceScope because " + - "it is not detached."); - } - detached = false; - } - - @Override - public void close() { - tracer.popNullScope(); - } - - @Override - public String toString() { - return "NullScope"; - } - - @Override - public void addKVAnnotation(String key, String value) { - // do nothing - } - - @Override - public void addTimelineAnnotation(String msg) { - // do nothing - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java deleted file mode 100644 index 34322fa..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.io.IOException; -import java.util.Collection; -import java.util.HashSet; - -/** - * SpanReceiver for testing only that just collects the Span objects it - * receives. The spans it receives can be accessed with getSpans(); - */ -public class POJOSpanReceiver extends SpanReceiver { - private final Collection spans; - - public POJOSpanReceiver(HTraceConfiguration conf) { - this.spans = new HashSet(); - } - - /** - * @return The spans this POJOSpanReceiver has received. - */ - public Collection getSpans() { - return spans; - } - - @Override - public void close() throws IOException { - } - - @Override - public void receiveSpan(Span span) { - spans.add(span); - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/ProbabilitySampler.java b/htrace-core/src/main/java/org/apache/htrace/core/ProbabilitySampler.java deleted file mode 100644 index c0bb16c..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/ProbabilitySampler.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.util.concurrent.ThreadLocalRandom; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Sampler that returns true a certain percentage of the time. Specify the frequency interval by - * configuring a {@code double} value for {@link #SAMPLER_FRACTION_CONF_KEY}. - */ -public class ProbabilitySampler extends Sampler { - private static final Log LOG = LogFactory.getLog(ProbabilitySampler.class); - public final double threshold; - public final static String SAMPLER_FRACTION_CONF_KEY = "sampler.fraction"; - - public ProbabilitySampler(HTraceConfiguration conf) { - this.threshold = Double.parseDouble(conf.get(SAMPLER_FRACTION_CONF_KEY)); - if (LOG.isTraceEnabled()) { - LOG.trace("Created new ProbabilitySampler with threshold = " + - threshold + "."); - } - } - - @Override - public boolean next() { - return ThreadLocalRandom.current().nextDouble() < threshold; - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/Sampler.java b/htrace-core/src/main/java/org/apache/htrace/core/Sampler.java deleted file mode 100644 index af0165c..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/Sampler.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.lang.reflect.Constructor; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Extremely simple callback to determine the frequency that an action should be - * performed. - *

- * For example, the next() function may look like this: - *

- *

- * 
- * public boolean next() {
- *   return Math.random() > 0.5;
- * }
- * 
- * 
- * This would trace 50% of all gets, 75% of all puts and would not trace any other requests. - */ -public abstract class Sampler { - /** - * A {@link Sampler} builder. It takes a {@link Sampler} class name and - * constructs an instance of that class, with the provided configuration. - */ - public static class Builder { - private static final Log LOG = LogFactory.getLog(Builder.class); - - private final static String DEFAULT_PACKAGE = "org.apache.htrace.core"; - private final HTraceConfiguration conf; - private String className; - private ClassLoader classLoader = Builder.class.getClassLoader(); - - public Builder(HTraceConfiguration conf) { - this.conf = conf; - reset(); - } - - public Builder reset() { - this.className = null; - return this; - } - - public Builder className(String className) { - this.className = className; - return this; - } - - public Builder classLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - return this; - } - - private void throwError(String errorStr) { - LOG.error(errorStr); - throw new RuntimeException(errorStr); - } - - private void throwError(String errorStr, Throwable e) { - LOG.error(errorStr, e); - throw new RuntimeException(errorStr, e); - } - - public Sampler build() { - Sampler sampler = newSampler(); - if (LOG.isTraceEnabled()) { - LOG.trace("Created new sampler of type " + - sampler.getClass().getName(), new Exception()); - } - return sampler; - } - - private Sampler newSampler() { - if (className == null || className.isEmpty()) { - throwError("No sampler class specified."); - } - String str = className; - if (!str.contains(".")) { - str = DEFAULT_PACKAGE + "." + str; - } - Class cls = null; - try { - cls = classLoader.loadClass(str); - } catch (ClassNotFoundException e) { - throwError("Cannot find Sampler class " + str); - } - Constructor ctor = null; - try { - ctor = cls.getConstructor(HTraceConfiguration.class); - } catch (NoSuchMethodException e) { - throwError("Cannot find a constructor for class " + - str + "which takes an HTraceConfiguration."); - } - Sampler sampler = null; - try { - LOG.debug("Creating new instance of " + str + "..."); - sampler = ctor.newInstance(conf); - } catch (ReflectiveOperationException e) { - throwError("Reflection error when constructing " + - str + ".", e); - } catch (Throwable t) { - throwError("NewInstance error when constructing " + - str + ".", t); - } - return sampler; - } - } - - public static final Sampler ALWAYS = AlwaysSampler.INSTANCE; - public static final Sampler NEVER = NeverSampler.INSTANCE; - - public abstract boolean next(); -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/Span.java b/htrace-core/src/main/java/org/apache/htrace/core/Span.java deleted file mode 100644 index e63d414..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/Span.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -/** - * Base interface for gathering and reporting statistics about a block of - * execution. - *

- * Spans should form a directed acyclic graph structure. It should be possible - * to keep following the parents of a span until you arrive at a span with no - * parents.

- */ -@JsonSerialize(using = Span.SpanSerializer.class) -public interface Span { - /** - * The block has completed, stop the clock - */ - void stop(); - - /** - * Get the start time, in milliseconds - */ - long getStartTimeMillis(); - - /** - * Get the stop time, in milliseconds - */ - long getStopTimeMillis(); - - /** - * Return the total amount of time elapsed since start was called, if running, - * or difference between stop and start - */ - long getAccumulatedMillis(); - - /** - * Has the span been started and not yet stopped? - */ - boolean isRunning(); - - /** - * Return a textual description of this span.

- * - * Will never be null. - */ - String getDescription(); - - /** - * A pseudo-unique (random) number assigned to this span instance.

- * - * The spanId is immutable and cannot be changed. It is safe to access this - * from multiple threads. - */ - SpanId getSpanId(); - - /** - * Create a child span of this span with the given description - * @deprecated Since 4.0.0. Use {@link MilliSpan.Builder} - */ - @Deprecated - Span child(String description); - - @Override - String toString(); - - /** - * Returns the parent IDs of the span.

- * - * The array will be empty if there are no parents. - */ - SpanId[] getParents(); - - /** - * Set the parents of this span.

- * - * Any existing parents will be cleared by this call. - */ - void setParents(SpanId[] parents); - - /** - * Add a data annotation associated with this span - */ - void addKVAnnotation(String key, String value); - - /** - * Add a timeline annotation associated with this span - */ - void addTimelineAnnotation(String msg); - - /** - * Get data associated with this span (read only)

- * - * Will never be null. - */ - Map getKVAnnotations(); - - /** - * Get any timeline annotations (read only)

- * - * Will never be null. - */ - List getTimelineAnnotations(); - - /** - * Return a unique id for the process from which this Span originated.

- * - * Will never be null. - */ - String getTracerId(); - - /** - * Set the process id of a span. - */ - void setTracerId(String s); - - /** - * Serialize to Json - */ - String toJson(); - - public static class SpanSerializer extends JsonSerializer { - @Override - public void serialize(Span span, JsonGenerator jgen, SerializerProvider provider) - throws IOException { - jgen.writeStartObject(); - if (span.getSpanId().isValid()) { - jgen.writeStringField("a", span.getSpanId().toString()); - } - if (span.getStartTimeMillis() != 0) { - jgen.writeNumberField("b", span.getStartTimeMillis()); - } - if (span.getStopTimeMillis() != 0) { - jgen.writeNumberField("e", span.getStopTimeMillis()); - } - if (!span.getDescription().isEmpty()) { - jgen.writeStringField("d", span.getDescription()); - } - String tracerId = span.getTracerId(); - if (!tracerId.isEmpty()) { - jgen.writeStringField("r", tracerId); - } - jgen.writeArrayFieldStart("p"); - for (SpanId parent : span.getParents()) { - jgen.writeString(parent.toString()); - } - jgen.writeEndArray(); - Map traceInfoMap = span.getKVAnnotations(); - if (!traceInfoMap.isEmpty()) { - jgen.writeObjectFieldStart("n"); - for (Map.Entry e : traceInfoMap.entrySet()) { - jgen.writeStringField(e.getKey(), e.getValue()); - } - jgen.writeEndObject(); - } - List timelineAnnotations = - span.getTimelineAnnotations(); - if (!timelineAnnotations.isEmpty()) { - jgen.writeArrayFieldStart("t"); - for (TimelineAnnotation tl : timelineAnnotations) { - jgen.writeStartObject(); - jgen.writeNumberField("t", tl.getTime()); - jgen.writeStringField("m", tl.getMessage()); - jgen.writeEndObject(); - } - jgen.writeEndArray(); - } - jgen.writeEndObject(); - } - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/SpanId.java b/htrace-core/src/main/java/org/apache/htrace/core/SpanId.java deleted file mode 100644 index ed31ad3..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/SpanId.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.util.concurrent.ThreadLocalRandom; - -/** - * Uniquely identifies an HTrace span. - * - * Span IDs are 128 bits in total. The upper 64 bits of a span ID is the same - * as the upper 64 bits of the parent span, if there is one. The lower 64 bits - * are always random. - */ -public final class SpanId implements Comparable { - private static final int SPAN_ID_STRING_LENGTH = 32; - private final long high; - private final long low; - - /** - * The invalid span ID, which is all zeroes. - * - * It is also the "least" span ID in the sense that it is considered - * smaller than any other span ID. - */ - public static SpanId INVALID = new SpanId(0, 0); - - private static long nonZeroRand64() { - while (true) { - long r = ThreadLocalRandom.current().nextLong(); - if (r != 0) { - return r; - } - } - } - - public static SpanId fromRandom() { - return new SpanId(nonZeroRand64(), nonZeroRand64()); - } - - public static SpanId fromString(String str) { - if (str.length() != SPAN_ID_STRING_LENGTH) { - throw new RuntimeException("Invalid SpanID string: length was not " + - SPAN_ID_STRING_LENGTH); - } - long high = - ((Long.parseLong(str.substring(0, 8), 16)) << 32) | - (Long.parseLong(str.substring(8, 16), 16)); - long low = - ((Long.parseLong(str.substring(16, 24), 16)) << 32) | - (Long.parseLong(str.substring(24, 32), 16)); - return new SpanId(high, low); - } - - public SpanId(long high, long low) { - this.high = high; - this.low = low; - } - - public long getHigh() { - return high; - } - - public long getLow() { - return low; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof SpanId)) { - return false; - } - SpanId other = (SpanId)o; - return ((other.high == high) && (other.low == low)); - } - - @Override - public int compareTo(SpanId other) { - int cmp = compareAsUnsigned(high, other.high); - if (cmp != 0) { - return cmp; - } - return compareAsUnsigned(low, other.low); - } - - private static int compareAsUnsigned(long a, long b) { - boolean aSign = a < 0; - boolean bSign = b < 0; - if (aSign != bSign) { - if (aSign) { - return 1; - } else { - return -1; - } - } - if (aSign) { - a = -a; - b = -b; - } - if (a < b) { - return -1; - } else if (a > b) { - return 1; - } else { - return 0; - } - } - - @Override - public int hashCode() { - return (int)((0xffffffff & (high >> 32))) ^ - (int)((0xffffffff & (high >> 0))) ^ - (int)((0xffffffff & (low >> 32))) ^ - (int)((0xffffffff & (low >> 0))); - } - - @Override - public String toString() { - return String.format("%08x%08x%08x%08x", - (0x00000000ffffffffL & (high >> 32)), - (0x00000000ffffffffL & high), - (0x00000000ffffffffL & (low >> 32)), - (0x00000000ffffffffL & low)); - } - - public boolean isValid() { - return (high != 0) || (low != 0); - } - - public SpanId newChildId() { - return new SpanId(high, nonZeroRand64()); - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/SpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/core/SpanReceiver.java deleted file mode 100644 index a955ddf..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/SpanReceiver.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.io.Closeable; -import java.lang.reflect.Constructor; -import java.util.concurrent.atomic.AtomicLong; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * The collector within a process that is the destination of Spans when a trace is running. - * {@code SpanReceiver} implementations are expected to provide a constructor with the signature - *

- *

- * public SpanReceiverImpl(HTraceConfiguration)
- * 
- */ -public abstract class SpanReceiver implements Closeable { - /** - * A {@link SpanReceiver} builder. It takes a {@link SpanReceiver} class name - * and constructs an instance of that class, with the provided configuration. - */ - public static class Builder { - private static final Log LOG = LogFactory.getLog(Builder.class); - - private final static String DEFAULT_PACKAGE = "org.apache.htrace.core"; - private final HTraceConfiguration conf; - private boolean logErrors; - private String className; - private ClassLoader classLoader = Builder.class.getClassLoader(); - - public Builder(HTraceConfiguration conf) { - this.conf = conf; - reset(); - } - - /** - * Set this builder back to defaults. - * - * @return this instance. - */ - public Builder reset() { - this.logErrors = true; - this.className = null; - return this; - } - - public Builder className(final String className) { - this.className = className; - return this; - } - - /** - * Configure whether we should log errors during build(). - * @return This instance - */ - public Builder logErrors(boolean logErrors) { - this.logErrors = logErrors; - return this; - } - - public Builder classLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - return this; - } - - private void throwError(String errorStr) { - if (logErrors) { - LOG.error(errorStr); - } - throw new RuntimeException(errorStr); - } - - private void throwError(String errorStr, Throwable e) { - if (logErrors) { - LOG.error(errorStr, e); - } - throw new RuntimeException(errorStr, e); - } - - public SpanReceiver build() { - SpanReceiver spanReceiver = newSpanReceiver(); - if (LOG.isTraceEnabled()) { - LOG.trace("Created new span receiver of type " + - spanReceiver.getClass().getName()); - } - return spanReceiver; - } - - private SpanReceiver newSpanReceiver() { - if ((className == null) || className.isEmpty()) { - throwError("No span receiver class specified."); - } - String str = className; - if (!str.contains(".")) { - str = DEFAULT_PACKAGE + "." + str; - } - Class cls = null; - try { - cls = classLoader.loadClass(str); - } catch (ClassNotFoundException e) { - throwError("Cannot find SpanReceiver class " + str); - } - Constructor ctor = null; - try { - ctor = cls.getConstructor(HTraceConfiguration.class); - } catch (NoSuchMethodException e) { - throwError("Cannot find a constructor for class " + - str + "which takes an HTraceConfiguration."); - } - SpanReceiver receiver = null; - try { - LOG.debug("Creating new instance of " + str + "..."); - receiver = ctor.newInstance(conf); - } catch (ReflectiveOperationException e) { - throwError("Reflection error when constructing " + - str + ".", e); - } catch (Throwable t) { - throwError("NewInstance error when constructing " + - str + ".", t); - } - return receiver; - } - } - - /** - * An ID which uniquely identifies this SpanReceiver. - */ - private final long id; - - private static final AtomicLong HIGHEST_SPAN_RECEIVER_ID = new AtomicLong(0); - - /** - * Get an ID uniquely identifying this SpanReceiver. - */ - public final long getId() { - return id; - } - - protected SpanReceiver() { - this.id = HIGHEST_SPAN_RECEIVER_ID.incrementAndGet(); - } - - /** - * Called when a Span is stopped and can now be stored. - */ - public abstract void receiveSpan(Span span); -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java deleted file mode 100644 index f443ec6..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.io.IOException; - -/** - * Used for testing. Simply prints to standard out any spans it receives. - */ -public class StandardOutSpanReceiver extends SpanReceiver { - private static final Log LOG = LogFactory.getLog(StandardOutSpanReceiver.class); - - public StandardOutSpanReceiver(HTraceConfiguration conf) { - LOG.trace("Created new StandardOutSpanReceiver."); - } - - @Override - public void receiveSpan(Span span) { - System.out.println(span); - } - - @Override - public void close() throws IOException { - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TimelineAnnotation.java b/htrace-core/src/main/java/org/apache/htrace/core/TimelineAnnotation.java deleted file mode 100644 index 18de061..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/TimelineAnnotation.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -public class TimelineAnnotation { - private final long time; - private final String msg; - - public TimelineAnnotation(long time, String msg) { - this.time = time; - this.msg = msg; - } - - public long getTime() { - return time; - } - - public String getMessage() { - return msg; - } - - @Override - public String toString() { - return "@" + time + ": " + msg; - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TraceCallable.java b/htrace-core/src/main/java/org/apache/htrace/core/TraceCallable.java deleted file mode 100644 index a0fec17..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/TraceCallable.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.util.concurrent.Callable; - -/** - * Wrap a Callable with a Span that survives a change in threads. - */ -public class TraceCallable implements Callable { - private final Tracer tracer; - private final Callable impl; - private final TraceScope parent; - private final String description; - - TraceCallable(Tracer tracer, TraceScope parent, Callable impl, - String description) { - this.tracer = tracer; - this.impl = impl; - this.parent = parent; - if (description == null) { - this.description = Thread.currentThread().getName(); - } else { - this.description = description; - } - } - - @Override - public V call() throws Exception { - TraceScope chunk = tracer.newScope(description, - parent.getSpan().getSpanId()); - try { - return impl.call(); - } finally { - chunk.close(); - } - } - - public Callable getImpl() { - return impl; - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TraceExecutorService.java b/htrace-core/src/main/java/org/apache/htrace/core/TraceExecutorService.java deleted file mode 100644 index 81e31ea..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/TraceExecutorService.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -public class TraceExecutorService implements ExecutorService { - private final Tracer tracer; - private final String scopeName; - private final ExecutorService impl; - - TraceExecutorService(Tracer tracer, String scopeName, - ExecutorService impl) { - this.tracer = tracer; - this.scopeName = scopeName; - this.impl = impl; - } - - @Override - public void execute(Runnable command) { - impl.execute(tracer.wrap(command, scopeName)); - } - - @Override - public void shutdown() { - impl.shutdown(); - } - - @Override - public List shutdownNow() { - return impl.shutdownNow(); - } - - @Override - public boolean isShutdown() { - return impl.isShutdown(); - } - - @Override - public boolean isTerminated() { - return impl.isTerminated(); - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) - throws InterruptedException { - return impl.awaitTermination(timeout, unit); - } - - @Override - public Future submit(Callable task) { - return impl.submit(tracer.wrap(task, scopeName)); - } - - @Override - public Future submit(Runnable task, T result) { - return impl.submit(tracer.wrap(task, scopeName), result); - } - - @Override - public Future submit(Runnable task) { - return impl.submit(tracer.wrap(task, scopeName)); - } - - private Collection> wrapCollection( - Collection> tasks) { - List> result = new ArrayList>(); - for (Callable task : tasks) { - result.add(tracer.wrap(task, scopeName)); - } - return result; - } - - @Override - public List> invokeAll(Collection> tasks) - throws InterruptedException { - return impl.invokeAll(wrapCollection(tasks)); - } - - @Override - public List> invokeAll(Collection> tasks, - long timeout, TimeUnit unit) throws InterruptedException { - return impl.invokeAll(wrapCollection(tasks), timeout, unit); - } - - @Override - public T invokeAny(Collection> tasks) - throws InterruptedException, ExecutionException { - return impl.invokeAny(wrapCollection(tasks)); - } - - @Override - public T invokeAny(Collection> tasks, long timeout, - TimeUnit unit) throws InterruptedException, ExecutionException, - TimeoutException { - return impl.invokeAny(wrapCollection(tasks), timeout, unit); - } - -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TraceRunnable.java b/htrace-core/src/main/java/org/apache/htrace/core/TraceRunnable.java deleted file mode 100644 index 8f98708..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/TraceRunnable.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -/** - * Wrap a Runnable with a Span that survives a change in threads. - */ -public class TraceRunnable implements Runnable { - private final Tracer tracer; - private final TraceScope parent; - private final Runnable runnable; - private final String description; - - public TraceRunnable(Tracer tracer, TraceScope parent, - Runnable runnable, String description) { - this.tracer = tracer; - this.parent = parent; - this.runnable = runnable; - if (description == null) { - this.description = Thread.currentThread().getName(); - } else { - this.description = description; - } - } - - @Override - public void run() { - TraceScope chunk = tracer.newScope(description, - parent.getSpan().getSpanId()); - try { - runnable.run(); - } finally { - chunk.close(); - } - } - - public Runnable getRunnable() { - return runnable; - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TraceScope.java b/htrace-core/src/main/java/org/apache/htrace/core/TraceScope.java deleted file mode 100644 index 05a053e..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/TraceScope.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.io.Closeable; - -/** - * Create a new TraceScope at major transitions. Hosts current tracing context. - */ -public class TraceScope implements Closeable { - /** - * The tracer to use for this scope. - */ - final Tracer tracer; - - /** - * The trace span for this scope, or null if the scope is closed. - * - * If the scope is closed, it must also be detached. - */ - private final Span span; - - /** - * The parent of this trace scope, or null if there is no parent. - */ - private TraceScope parent; - - /** - * True if this scope is detached. - */ - boolean detached; - - TraceScope(Tracer tracer, Span span, TraceScope parent) { - this.tracer = tracer; - this.span = span; - this.parent = parent; - this.detached = false; - } - - /** - * Returns the span which this scope is managing. - */ - public Span getSpan() { - return span; - } - - /** - * Returns the span ID which this scope is managing. - */ - public SpanId getSpanId() { - return span.getSpanId(); - } - - TraceScope getParent() { - return parent; - } - - void setParent(TraceScope parent) { - this.parent = parent; - } - - /** - * Detach this TraceScope from the current thread. - * - * It is OK to "leak" TraceScopes which have been detached. They will not - * consume any resources other than a small amount of memory until they are - * garbage collected. On the other hand, trace scopes which are still - * attached must never be leaked. - */ - public void detach() { - if (detached) { - Tracer.throwClientError("Can't detach this TraceScope because " + - "it is already detached."); - } - tracer.detachScope(this); - detached = true; - parent = null; - } - - /** - * Attach this TraceScope to the current thread. - */ - public void reattach() { - if (!detached) { - Tracer.throwClientError("Can't reattach this TraceScope because " + - "it is not detached."); - } - tracer.reattachScope(this); - detached = false; - } - - /** - * Close this TraceScope, ending the trace span it is managing. - */ - @Override - public void close() { - tracer.closeScope(this); - } - - public void addKVAnnotation(String key, String value) { - span.addKVAnnotation(key, value); - } - - public void addTimelineAnnotation(String msg) { - span.addTimelineAnnotation(msg); - } - - @Override - public String toString() { - return "TraceScope(tracerId=" + tracer.getTracerId() + - ", span=" + span.toJson() + - ", detached=" + detached + ")"; - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/Tracer.java b/htrace-core/src/main/java/org/apache/htrace/core/Tracer.java deleted file mode 100644 index 39d972d..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/Tracer.java +++ /dev/null @@ -1,673 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.io.Closeable; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Use a Tracer instance inside a 'process' to collect and distribute its trace Spans. - * Example processes are an HDFS DataNode or an HBase RegionServer. A Tracer instance is your - * one-stop shop for all things tracing. - * - *

- */ -public class Tracer implements Closeable { - private static final Log LOG = LogFactory.getLog(Tracer.class); - - public final static String SPAN_RECEIVER_CLASSES_KEY = "span.receiver.classes"; - public final static String SAMPLER_CLASSES_KEY = "sampler.classes"; - - public static class Builder { - private String name; - private HTraceConfiguration conf = HTraceConfiguration.EMPTY; - private ClassLoader classLoader = - Builder.class.getClassLoader(); - private TracerPool tracerPool = TracerPool.GLOBAL; - - /** - * @deprecated Since 4.0.0. Use Constructor that takes a name argument instead - */ - @Deprecated - public Builder() { - } - - public Builder(final String name) { - name(name); - } - - /** - * @param name - * @return This - * @deprecated Since 4.0.0. Use Constructor that takes a name argument instead. - */ - @Deprecated - public Builder name(String name) { - this.name = name; - return this; - } - - public Builder conf(HTraceConfiguration conf) { - this.conf = conf; - return this; - } - - public Builder tracerPool(TracerPool tracerPool) { - this.tracerPool = tracerPool; - return this; - } - - private void loadSamplers(List samplers) { - String classNamesStr = conf.get(SAMPLER_CLASSES_KEY, ""); - List classNames = getClassNamesFromConf(classNamesStr); - StringBuilder bld = new StringBuilder(); - String prefix = ""; - for (String className : classNames) { - try { - Sampler sampler = new Sampler.Builder(conf). - className(className). - classLoader(classLoader). - build(); - samplers.add(sampler); - bld.append(prefix).append(className); - prefix = ", "; - } catch (Throwable e) { - LOG.error("Failed to create SpanReceiver of type " + className, e); - } - } - String resultString = bld.toString(); - if (resultString.isEmpty()) { - resultString = "no samplers"; - } - LOG.debug(SAMPLER_CLASSES_KEY + " = " + classNamesStr + - "; loaded " + resultString); - } - - private void loadSpanReceivers() { - String classNamesStr = conf.get(SPAN_RECEIVER_CLASSES_KEY, ""); - List classNames = getClassNamesFromConf(classNamesStr); - StringBuilder bld = new StringBuilder(); - String prefix = ""; - for (String className : classNames) { - try { - tracerPool.loadReceiverType(className, conf, classLoader); - bld.append(prefix).append(className); - prefix = ", "; - } catch (Throwable e) { - LOG.error("Failed to create SpanReceiver of type " + className, e); - } - } - String resultString = bld.toString(); - if (resultString.isEmpty()) { - resultString = "no span receivers"; - } - LOG.debug(SPAN_RECEIVER_CLASSES_KEY + " = " + classNamesStr + - "; loaded " + resultString); - } - - /** - * Get a list of class names from the HTrace configuration. - * Entries which are empty will be removed. Entries which lack a package will - * be given the default package. - * - * @param classNamesStr A semicolon-separated string containing a list - * of class names. - * @return A list of class names. - */ - private List getClassNamesFromConf(String classNamesStr) { - String classNames[] = classNamesStr.split(";"); - LinkedList cleanedClassNames = new LinkedList(); - for (String className : classNames) { - String cleanedClassName = className.trim(); - if (!cleanedClassName.isEmpty()) { - cleanedClassNames.add(cleanedClassName); - } - } - return cleanedClassNames; - } - - public Tracer build() { - if (name == null) { - throw new RuntimeException("You must specify a name for this Tracer."); - } - LinkedList samplers = new LinkedList(); - loadSamplers(samplers); - String tracerId = new TracerId(conf, name).get(); - Tracer tracer = new Tracer(tracerId, tracerPool, - samplers.toArray(new Sampler[samplers.size()])); - tracerPool.addTracer(tracer); - loadSpanReceivers(); - if (LOG.isTraceEnabled()) { - LOG.trace("Created " + tracer + " for " + name); - } - return tracer; - } - } - - /** - * The thread-specific context for this Tracer. - * - * This tracks the current number of trace scopes in a particular thread - * created by this tracer. We use this to apply our samplers only for the - * "top-level" spans. - * - * Note that we can't put the TraceScope objects themselves in this context, - * since we need to be able to use TraceScopes created by other Tracers, and - * this context is per-Tracer. - */ - private static class ThreadContext { - private long depth; - - ThreadContext() { - this.depth = 0; - } - - boolean isTopLevel() { - return (depth == 0); - } - - void pushScope() { - depth++; - } - - TraceScope pushNewScope(Tracer tracer, Span span, TraceScope parentScope) { - TraceScope scope = new TraceScope(tracer, span, parentScope); - threadLocalScope.set(scope); - depth++; - return scope; - } - - void popScope() { - if (depth <= 0) { - throwClientError("There were more trace scopes closed than " + - "were opened."); - } - depth--; - } - }; - - /** - * A subclass of ThreadLocal that starts off with a non-null initial value in - * each thread. - */ - private static class ThreadLocalContext extends ThreadLocal { - @Override - protected ThreadContext initialValue() { - return new ThreadContext(); - } - }; - - /** - * The current trace scope. This is global, so it is shared amongst all - * libraries using HTrace. - */ - final static ThreadLocal threadLocalScope = - new ThreadLocal(); - - /** - * An empty array of SpanId objects. Can be used rather than constructing a - * new object whenever we need an empty array. - */ - private static final SpanId EMPTY_PARENT_ARRAY[] = new SpanId[0]; - - /** - * The tracerId. - */ - private final String tracerId; - - /** - * The TracerPool which this Tracer belongs to. - * - * This gets set to null after the Tracer is closed in order to catch some - * use-after-close errors. Note that we do not synchronize access on this - * field, since it only changes when the Tracer is closed, and the Tracer - * should not be used after that. - */ - private TracerPool tracerPool; - - /** - * The current thread-local context for this particualr Tracer. - */ - private final ThreadLocalContext threadContext; - - /** - * The NullScope instance for this Tracer. - */ - private final NullScope nullScope; - - /** - * The currently active Samplers. - * - * Arrays are immutable once set. You must take the Tracer lock in order to - * set this to a new array. If this is null, the Tracer is closed. - */ - private volatile Sampler[] curSamplers; - - /** - * Log a client error, and throw an exception. - * - * @param str The message to use in the log and the exception. - */ - static void throwClientError(String str) { - LOG.error(str); - throw new RuntimeException(str); - } - - /** - * @return If the current thread is tracing, this function returns the Tracer that is - * being used; otherwise, it returns null. - */ - public static Tracer curThreadTracer() { - TraceScope traceScope = threadLocalScope.get(); - if (traceScope == null) { - return null; - } - return traceScope.tracer; - } - - Tracer(String tracerId, TracerPool tracerPool, Sampler[] curSamplers) { - this.tracerId = tracerId; - this.tracerPool = tracerPool; - this.threadContext = new ThreadLocalContext(); - this.nullScope = new NullScope(this); - this.curSamplers = curSamplers; - } - - public String getTracerId() { - return tracerId; - } - - private TraceScope newScopeImpl(ThreadContext context, String description) { - Span span = new MilliSpan.Builder(). - tracerId(tracerId). - begin(System.currentTimeMillis()). - description(description). - parents(EMPTY_PARENT_ARRAY). - spanId(SpanId.fromRandom()). - build(); - return context.pushNewScope(this, span, null); - } - - private TraceScope newScopeImpl(ThreadContext context, String description, - TraceScope parentScope) { - SpanId parentId = parentScope.getSpan().getSpanId(); - Span span = new MilliSpan.Builder(). - tracerId(tracerId). - begin(System.currentTimeMillis()). - description(description). - parents(new SpanId[] { parentId }). - spanId(parentId.newChildId()). - build(); - return context.pushNewScope(this, span, parentScope); - } - - private TraceScope newScopeImpl(ThreadContext context, String description, - SpanId parentId) { - Span span = new MilliSpan.Builder(). - tracerId(tracerId). - begin(System.currentTimeMillis()). - description(description). - parents(new SpanId[] { parentId }). - spanId(parentId.newChildId()). - build(); - return context.pushNewScope(this, span, null); - } - - private TraceScope newScopeImpl(ThreadContext context, String description, - TraceScope parentScope, SpanId secondParentId) { - SpanId parentId = parentScope.getSpan().getSpanId(); - Span span = new MilliSpan.Builder(). - tracerId(tracerId). - begin(System.currentTimeMillis()). - description(description). - parents(new SpanId[] { parentId, secondParentId }). - spanId(parentId.newChildId()). - build(); - return context.pushNewScope(this, span, parentScope); - } - - /** - * Create a new trace scope. - * - * If there are no scopes above the current scope, we will apply our - * configured samplers. Otherwise, we will create a trace Span only if this thread - * is already tracing, or if the passed parentID was valid. - * - * @param description The description of the new span to create. - * @param parentId If this is a valid span ID, it will be added to - * the parents of the new span we create. - * @return The new trace scope. - */ - public TraceScope newScope(String description, SpanId parentId) { - TraceScope parentScope = threadLocalScope.get(); - ThreadContext context = threadContext.get(); - if (parentScope != null) { - if (parentId.isValid() && - (!parentId.equals(parentScope.getSpan().getSpanId()))) { - return newScopeImpl(context, description, parentScope, parentId); - } else { - return newScopeImpl(context, description, parentScope); - } - } else if (parentId.isValid()) { - return newScopeImpl(context, description, parentId); - } - if (!context.isTopLevel()) { - context.pushScope(); - return nullScope; - } - if (!sample()) { - context.pushScope(); - return nullScope; - } - return newScopeImpl(context, description); - } - - /** - * Create a new trace scope. - * - * If there are no scopes above the current scope, we will apply our - * configured samplers. Otherwise, we will create a trace Span only if this thread - * is already tracing. - * @param description The description of the new span to create. - * @return The new trace scope. - */ - public TraceScope newScope(String description) { - TraceScope parentScope = threadLocalScope.get(); - ThreadContext context = threadContext.get(); - if (parentScope != null) { - return newScopeImpl(context, description, parentScope); - } - if (!context.isTopLevel()) { - context.pushScope(); - return nullScope; - } - if (!sample()) { - context.pushScope(); - return nullScope; - } - return newScopeImpl(context, description); - } - - /** - * Return a null trace scope. - */ - public TraceScope newNullScope() { - ThreadContext context = threadContext.get(); - context.pushScope(); - return nullScope; - } - - /** - * Wrap the callable in a TraceCallable, if tracing. - * - * @return The callable provided, wrapped if tracing, 'callable' if not. - */ - public Callable wrap(Callable callable, String description) { - TraceScope parentScope = threadLocalScope.get(); - if (parentScope == null) { - return callable; - } - return new TraceCallable(this, parentScope, callable, description); - } - - /** - * Wrap the runnable in a TraceRunnable, if tracing - * - * @return The runnable provided, wrapped if tracing, 'runnable' if not. - */ - public Runnable wrap(Runnable runnable, String description) { - TraceScope parentScope = threadLocalScope.get(); - if (parentScope == null) { - return runnable; - } - return new TraceRunnable(this, parentScope, runnable, description); - } - - public TraceExecutorService newTraceExecutorService(ExecutorService impl, - String scopeName) { - return new TraceExecutorService(this, scopeName, impl); - } - - public TracerPool getTracerPool() { - if (tracerPool == null) { - throwClientError(toString() + " is closed."); - } - return tracerPool; - } - - /** - * Returns an object that will trace all calls to itself. - */ - @SuppressWarnings("unchecked") - T createProxy(final T instance) { - final Tracer tracer = this; - InvocationHandler handler = new InvocationHandler() { - @Override - public Object invoke(Object obj, Method method, Object[] args) - throws Throwable { - TraceScope scope = tracer.newScope(method.getName()); - try { - return method.invoke(instance, args); - } catch (Throwable ex) { - ex.printStackTrace(); - throw ex; - } finally { - scope.close(); - } - } - }; - return (T) Proxy.newProxyInstance(instance.getClass().getClassLoader(), - instance.getClass().getInterfaces(), handler); - } - - /** - * Return true if we should create a new top-level span. - * - * We will create the span if any configured sampler returns true. - */ - private boolean sample() { - Sampler[] samplers = curSamplers; - for (Sampler sampler : samplers) { - if (sampler.next()) { - return true; - } - } - return false; - } - - /** - * Returns an array of all the current Samplers. - * - * Note that if the current Samplers change, those changes will not be - * reflected in this array. In other words, this array may be stale. - */ - public Sampler[] getSamplers() { - return curSamplers; - } - - /** - * Add a new Sampler. - * - * @param sampler The new sampler to add. - * You cannot add a particular Sampler object more - * than once. You may add multiple Sampler objects - * of the same type, although this is not recommended. - * - * @return True if the sampler was added; false if it already had - * been added earlier. - */ - public synchronized boolean addSampler(Sampler sampler) { - if (tracerPool == null) { - throwClientError(toString() + " is closed."); - } - Sampler[] samplers = curSamplers; - for (int i = 0; i < samplers.length; i++) { - if (samplers[i] == sampler) { - return false; - } - } - Sampler[] newSamplers = - Arrays.copyOf(samplers, samplers.length + 1); - newSamplers[samplers.length] = sampler; - curSamplers = newSamplers; - return true; - } - - /** - * Remove a SpanReceiver. - * - * @param sampler The sampler to remove. - */ - public synchronized boolean removeSampler(Sampler sampler) { - if (tracerPool == null) { - throwClientError(toString() + " is closed."); - } - Sampler[] samplers = curSamplers; - for (int i = 0; i < samplers.length; i++) { - if (samplers[i] == sampler) { - Sampler[] newSamplers = new Sampler[samplers.length - 1]; - System.arraycopy(samplers, 0, newSamplers, 0, i); - System.arraycopy(samplers, i + 1, newSamplers, i, - samplers.length - i - 1); - curSamplers = newSamplers; - return true; - } - } - return false; - } - - void detachScope(TraceScope scope) { - TraceScope curScope = threadLocalScope.get(); - if (curScope != scope) { - throwClientError("Can't detach TraceScope for " + - scope.getSpan().toJson() + " because it is not the current " + - "TraceScope in thread " + Thread.currentThread().getName()); - } - ThreadContext context = threadContext.get(); - context.popScope(); - threadLocalScope.set(scope.getParent()); - } - - void reattachScope(TraceScope scope) { - TraceScope parent = threadLocalScope.get(); - Tracer.threadLocalScope.set(scope); - ThreadContext context = threadContext.get(); - context.pushScope(); - scope.setParent(parent); - } - - void closeScope(TraceScope scope) { - TraceScope curScope = threadLocalScope.get(); - if (curScope != scope) { - throwClientError("Can't close TraceScope for " + - scope.getSpan().toJson() + " because it is not the current " + - "TraceScope in thread " + Thread.currentThread().getName()); - } - if (tracerPool == null) { - throwClientError(toString() + " is closed."); - } - SpanReceiver[] receivers = tracerPool.getReceivers(); - if (receivers == null) { - throwClientError(toString() + " is closed."); - } - ThreadContext context = threadContext.get(); - context.popScope(); - threadLocalScope.set(scope.getParent()); - scope.setParent(null); - Span span = scope.getSpan(); - span.stop(); - for (SpanReceiver receiver : receivers) { - receiver.receiveSpan(span); - } - } - - void popNullScope() { - TraceScope curScope = threadLocalScope.get(); - if (curScope != null) { - throwClientError("Attempted to close an empty scope, but it was not " + - "the current thread scope in thread " + - Thread.currentThread().getName()); - } - ThreadContext context = threadContext.get(); - context.popScope(); - } - - public static Span getCurrentSpan() { - TraceScope curScope = threadLocalScope.get(); - if (curScope == null) { - return null; - } else { - return curScope.getSpan(); - } - } - - public static SpanId getCurrentSpanId() { - TraceScope curScope = threadLocalScope.get(); - if (curScope == null) { - return SpanId.INVALID; - } else { - return curScope.getSpan().getSpanId(); - } - } - - @Override - public synchronized void close() { - if (tracerPool == null) { - return; - } - curSamplers = new Sampler[0]; - tracerPool.removeTracer(this); - } - - /** - * Get the hash code of a Tracer object. - * - * This hash code is based on object identity. - * This is used in TracerPool to create a hash table of Tracers. - */ - @Override - public int hashCode() { - return System.identityHashCode(this); - } - - /** - * Compare two tracer objects. - * - * Tracer objects are always compared by object equality. - * This is used in TracerPool to create a hash table of Tracers. - */ - @Override - public boolean equals(Object other) { - return (this == other); - } - - @Override - public String toString() { - return "Tracer(" + tracerId + ")"; - } - -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TracerId.java b/htrace-core/src/main/java/org/apache/htrace/core/TracerId.java deleted file mode 100644 index da482fe..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/TracerId.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.management.ManagementFactory; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Enumeration; -import java.util.Locale; -import java.util.TreeSet; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * The HTrace tracer ID.

- * - * HTrace tracer IDs are created from format strings. - * Format strings contain variables which the TracerId class will - * replace with the correct values at runtime.

- * - *

    - *
  • %{tname}: the tracer name supplied when creating the Tracer.
  • - *
  • %{pname}: the process name obtained from the JVM.
  • - *
  • %{ip}: will be replaced with an ip address.
  • - *
  • %{pid}: the numerical process ID from the operating system.
  • - *

- * - * For example, the string "%{pname}/%{ip}" will be replaced with something - * like: DataNode/192.168.0.1, assuming that the process' name is DataNode - * and its IP address is 192.168.0.1.

- * - * ID strings can contain backslashes as escapes. - * For example, "\a" will map to "a". "\%{ip}" will map to the literal - * string "%{ip}", not the IP address. A backslash itself can be escaped by a - * preceding backslash. - */ -public final class TracerId { - private static final Log LOG = LogFactory.getLog(TracerId.class); - - /** - * The configuration key to use for process id - */ - public static final String TRACER_ID_KEY = "tracer.id"; - - /** - * The default tracer ID to use if no other ID is configured. - */ - private static final String DEFAULT_TRACER_ID = "%{tname}/%{ip}"; - - private final String tracerName; - - private final String tracerId; - - public TracerId(HTraceConfiguration conf, String tracerName) { - this.tracerName = tracerName; - String fmt = conf.get(TRACER_ID_KEY, DEFAULT_TRACER_ID); - StringBuilder bld = new StringBuilder(); - StringBuilder varBld = null; - boolean escaping = false; - int varSeen = 0; - for (int i = 0, len = fmt.length() ; i < len; i++) { - char c = fmt.charAt(i); - if (c == '\\') { - if (!escaping) { - escaping = true; - continue; - } - } - switch (varSeen) { - case 0: - if (c == '%') { - if (!escaping) { - varSeen = 1; - continue; - } - } - escaping = false; - varSeen = 0; - bld.append(c); - break; - case 1: - if (c == '{') { - if (!escaping) { - varSeen = 2; - varBld = new StringBuilder(); - continue; - } - } - escaping = false; - varSeen = 0; - bld.append("%").append(c); - break; - default: - if (c == '}') { - if (!escaping) { - String var = varBld.toString(); - bld.append(processShellVar(var)); - varBld = null; - varSeen = 0; - continue; - } - } - escaping = false; - varBld.append(c); - varSeen++; - break; - } - } - if (varSeen > 0) { - LOG.warn("Unterminated process ID substitution variable at the end " + - "of format string " + fmt); - } - this.tracerId = bld.toString(); - if (LOG.isTraceEnabled()) { - LOG.trace("ProcessID(fmt=" + fmt + "): computed process ID of \"" + - this.tracerId + "\""); - } - } - - private String processShellVar(String var) { - if (var.equals("tname")) { - return tracerName; - } else if (var.equals("pname")) { - return getProcessName(); - } else if (var.equals("ip")) { - return getBestIpString(); - } else if (var.equals("pid")) { - return Long.valueOf(getOsPid()).toString(); - } else { - LOG.warn("unknown ProcessID variable " + var); - return ""; - } - } - - static String getProcessName() { - String cmdLine = System.getProperty("sun.java.command"); - if (cmdLine != null && !cmdLine.isEmpty()) { - String fullClassName = cmdLine.split("\\s+")[0]; - String[] classParts = fullClassName.split("\\."); - cmdLine = classParts[classParts.length - 1]; - } - return (cmdLine == null || cmdLine.isEmpty()) ? "Unknown" : cmdLine; - } - - /** - * Get the best IP address that represents this node.

- * - * This is complicated since nodes can have multiple network interfaces, - * and each network interface can have multiple IP addresses. What we're - * looking for here is an IP address that will serve to identify this node - * to HTrace. So we prefer site-local addresess (i.e. private ones on the - * LAN) to publicly routable interfaces. If there are multiple addresses - * to choose from, we select the one which comes first in textual sort - * order. This should ensure that we at least consistently call each node - * by a single name. - */ - static String getBestIpString() { - Enumeration ifaces; - try { - ifaces = NetworkInterface.getNetworkInterfaces(); - } catch (SocketException e) { - LOG.error("Error getting network interfaces", e); - return "127.0.0.1"; - } - TreeSet siteLocalCandidates = new TreeSet(); - TreeSet candidates = new TreeSet(); - while (ifaces.hasMoreElements()) { - NetworkInterface iface = ifaces.nextElement(); - for (Enumeration addrs = - iface.getInetAddresses(); addrs.hasMoreElements();) { - InetAddress addr = addrs.nextElement(); - if (!addr.isLoopbackAddress()) { - if (addr.isSiteLocalAddress()) { - siteLocalCandidates.add(addr.getHostAddress()); - } else { - candidates.add(addr.getHostAddress()); - } - } - } - } - if (!siteLocalCandidates.isEmpty()) { - return siteLocalCandidates.first(); - } - if (!candidates.isEmpty()) { - return candidates.first(); - } - return "127.0.0.1"; - } - - /** - * Get the process id from the operating system.

- * - * Unfortunately, there is no simple method to get the process id in Java. - * The approach we take here is to use the shell method (see - * {TracerId#getOsPidFromShellPpid}) unless we are on Windows, where the - * shell is not available. On Windows, we use - * {TracerId#getOsPidFromManagementFactory}, which depends on some - * undocumented features of the JVM, but which doesn't require a shell. - */ - static long getOsPid() { - if ((System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH)). - contains("windows")) { - return getOsPidFromManagementFactory(); - } else { - return getOsPidFromShellPpid(); - } - } - - /** - * Get the process ID by executing a shell and printing the PPID (parent - * process ID).

- * - * This method of getting the process ID doesn't depend on any undocumented - * features of the virtual machine, and should work on almost any UNIX - * operating system. - */ - private static long getOsPidFromShellPpid() { - Process p = null; - StringBuilder sb = new StringBuilder(); - try { - p = new ProcessBuilder("/usr/bin/env", "sh", "-c", "echo $PPID"). - redirectErrorStream(true).start(); - BufferedReader reader = new BufferedReader( - new InputStreamReader(p.getInputStream())); - String line = ""; - while ((line = reader.readLine()) != null) { - sb.append(line.trim()); - } - int exitVal = p.waitFor(); - if (exitVal != 0) { - throw new IOException("Process exited with error code " + - Integer.valueOf(exitVal).toString()); - } - } catch (InterruptedException e) { - LOG.error("Interrupted while getting operating system pid from " + - "the shell.", e); - return 0L; - } catch (IOException e) { - LOG.error("Error getting operating system pid from the shell.", e); - return 0L; - } finally { - if (p != null) { - p.destroy(); - } - } - try { - return Long.parseLong(sb.toString()); - } catch (NumberFormatException e) { - LOG.error("Error parsing operating system pid from the shell.", e); - return 0L; - } - } - - /** - * Get the process ID by looking at the name of the managed bean for the - * runtime system of the Java virtual machine.

- * - * Although this is undocumented, in the Oracle JVM this name is of the form - * [OS_PROCESS_ID]@[HOSTNAME]. - */ - private static long getOsPidFromManagementFactory() { - try { - return Long.parseLong(ManagementFactory.getRuntimeMXBean(). - getName().split("@")[0]); - } catch (NumberFormatException e) { - LOG.error("Failed to get the operating system process ID from the name " + - "of the managed bean for the JVM.", e); - return 0L; - } - } - - public String get() { - return tracerId; - } -} diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TracerPool.java b/htrace-core/src/main/java/org/apache/htrace/core/TracerPool.java deleted file mode 100644 index 26e39f5..0000000 --- a/htrace-core/src/main/java/org/apache/htrace/core/TracerPool.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.util.Arrays; -import java.util.HashSet; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * A pool of Tracer objects. - * - * There may be more than one {@link Tracer} running inside a single 'process'; for example, - * unit tests may spin up a DataNode, a NameNode, and HDFS clients all running in a single JVM - * instance, each with its own Tracer. TracerPool is where all Tracer instances register - * on creation so Tracers can coordinate around shared resources such as {@link SpanReceiver} - * instances. TracerPool takes care of properly cleaning up registered Tracer instances on shutdown. - */ -public class TracerPool { - private static final Log LOG = LogFactory.getLog(TracerPool.class); - - /** - * The global pool of tracer objects. - * - * This is the pool that new tracers get put into by default. - */ - static final TracerPool GLOBAL = new TracerPool("Global"); - - /** - * The shutdown hook which closes the Tracers in this pool when the process is - * shutting down. - */ - private class SpanReceiverShutdownHook extends Thread { - SpanReceiverShutdownHook() { - setName("SpanReceiverShutdownHook"); - setDaemon(false); - } - - @Override - public void run() { - removeAndCloseAllSpanReceivers(); - } - } - - /** - * The name of this TracerPool. - */ - private final String name; - - /** - * The current span receivers which these tracers are using. - * - * Can be read locklessly. Must be written under the lock. - * The array itself should never be modified. - */ - private volatile SpanReceiver[] curReceivers; - - /** - * The currently installed shutdown hook, or null if no hook has been - * installed. - */ - private SpanReceiverShutdownHook shutdownHook; - - /** - * The current Tracers. - */ - private final HashSet curTracers; - - /** - * Get the global tracer pool. - */ - public static TracerPool getGlobalTracerPool() { - return GLOBAL; - } - - public TracerPool(String name) { - this.name = name; - this.shutdownHook = null; - this.curTracers = new HashSet(); - this.curReceivers = new SpanReceiver[0]; - } - - /** - * Return the name of this TracerPool. - */ - public String getName() { - return name; - } - - /** - * Returns an array of all the current span receivers. - * - * Note that if the current span receivers change, those changes will not be - * reflected in this array. In other words, this array may be stale. - */ - public SpanReceiver[] getReceivers() { - return curReceivers; - } - - /** - * Add a new span receiver. - * - * @param receiver The new receiver to add. - * - * @return True if the new receiver was added; false if it - * already was there. - */ - public synchronized boolean addReceiver(SpanReceiver receiver) { - SpanReceiver[] receivers = curReceivers; - for (int i = 0; i < receivers.length; i++) { - if (receivers[i] == receiver) { - LOG.trace(toString() + ": can't add receiver " + receiver.toString() + - " since it is already in this pool."); - return false; - } - } - SpanReceiver[] newReceivers = - Arrays.copyOf(receivers, receivers.length + 1); - newReceivers[receivers.length] = receiver; - registerShutdownHookIfNeeded(); - curReceivers = newReceivers; - LOG.trace(toString() + ": added receiver " + receiver.toString()); - return true; - } - - /** - * Register the shutdown hook if needed. - */ - private synchronized void registerShutdownHookIfNeeded() { - if (shutdownHook != null) { - return; - } - shutdownHook = new SpanReceiverShutdownHook(); - Runtime.getRuntime().addShutdownHook(shutdownHook); - LOG.trace(toString() + ": registered shutdown hook."); - } - - /** - * Remove a span receiver. - * - * @param receiver The receiver to remove. - * - * @return True if the receiver was removed; false if it - * did not exist in this pool. - */ - public synchronized boolean removeReceiver(SpanReceiver receiver) { - SpanReceiver[] receivers = curReceivers; - for (int i = 0; i < receivers.length; i++) { - if (receivers[i] == receiver) { - SpanReceiver[] newReceivers = new SpanReceiver[receivers.length - 1]; - System.arraycopy(receivers, 0, newReceivers, 0, i); - System.arraycopy(receivers, i + 1, newReceivers, i, - receivers.length - i - 1); - curReceivers = newReceivers; - LOG.trace(toString() + ": removed receiver " + receiver.toString()); - return true; - } - } - LOG.trace(toString() + ": can't remove receiver " + receiver.toString() + - " since it's not currently in this pool."); - return false; - } - - /** - * Remove and close a span receiver. - * - * @param receiver The receiver to remove. - * - * @return True if the receiver was removed; false if it - * did not exist in this pool. - */ - public boolean removeAndCloseReceiver(SpanReceiver receiver) { - if (!removeReceiver(receiver)) { - return false; - } - try { - LOG.trace(toString() + ": closing receiver " + receiver.toString()); - receiver.close(); - } catch (Throwable t) { - LOG.error(toString() + ": error closing " + receiver.toString(), t); - } - return true; - } - - /** - * Remove and close all of the span receivers. - */ - private synchronized void removeAndCloseAllSpanReceivers() { - SpanReceiver[] receivers = curReceivers; - curReceivers = new SpanReceiver[0]; - for (SpanReceiver receiver : receivers) { - try { - LOG.trace(toString() + ": closing receiver " + receiver.toString()); - receiver.close(); - } catch (Throwable t) { - LOG.error(toString() + ": error closing " + receiver.toString(), t); - } - } - } - - /** - * Given a SpanReceiver class name, return the existing instance of that span - * receiver, if possible; otherwise, invoke the callable to create a new - * instance. - * - * @param className The span receiver class name. - * @param conf The HTrace configuration. - * @param classLoader The class loader to use. - * - * @return The SpanReceiver. - */ - public synchronized SpanReceiver loadReceiverType(String className, - HTraceConfiguration conf, ClassLoader classLoader) { - SpanReceiver[] receivers = curReceivers; - for (SpanReceiver receiver : receivers) { - if (receiver.getClass().getName().equals(className)) { - LOG.trace(toString() + ": returning a reference to receiver " + - receiver.toString()); - return receiver; - } - } - LOG.trace(toString() + ": creating a new SpanReceiver of type " + - className); - SpanReceiver receiver = new SpanReceiver.Builder(conf). - className(className). - classLoader(classLoader). - build(); - addReceiver(receiver); - return receiver; - } - - /** - * Returns an array of all the current Tracers. - * - * Note that if the current Tracers change, those changes will not be - * reflected in this array. In other words, this array may be stale. - */ - public synchronized Tracer[] getTracers() { - return curTracers.toArray(new Tracer[curTracers.size()]); - } - - /** - * Add a new Tracer. - */ - synchronized void addTracer(Tracer tracer) { - if (curTracers.add(tracer)) { - LOG.trace(toString() + ": adding tracer " + tracer.toString()); - } - } - - /** - * Remove a Tracer. - * - * If the Tracer removed was the last one, we will close all the SpanReceiver - * objects that we're managing. - */ - synchronized void removeTracer(Tracer tracer) { - if (curTracers.remove(tracer)) { - LOG.trace(toString() + ": removing tracer " + tracer.toString()); - if (curTracers.size() == 0) { - removeAndCloseAllSpanReceivers(); - } - } - } - - @Override - public String toString() { - return "TracerPool(" + name + ")"; - } -} \ No newline at end of file diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestBadClient.java b/htrace-core/src/test/java/org/apache/htrace/core/TestBadClient.java deleted file mode 100644 index 87ae8e9..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TestBadClient.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.io.File; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; - -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.hamcrest.CoreMatchers.containsString; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Test; - -public class TestBadClient { - @After - public void clearBadState() { - // Clear the bad trace state so that we don't disrupt other unit tests - // that run in this JVM. - Tracer.threadLocalScope.set(null); - } - - /** - * Test closing an outer scope when an inner one is still active. - */ - @Test - public void TestClosingOuterScope() throws Exception { - Tracer tracer = new Tracer.Builder(). - name("TestClosingOuterScopeTracer"). - tracerPool(new TracerPool("TestClosingOuterScope")). - conf(HTraceConfiguration. - fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build(); - boolean gotException = false; - TraceScope outerScope = tracer.newScope("outer"); - TraceScope innerScope = tracer.newScope("inner"); - try { - outerScope.close(); - } catch (RuntimeException e) { - assertThat(e.getMessage(), - containsString("it is not the current TraceScope")); - gotException = true; - } - assertTrue("Expected to get exception because of improper " + - "scope closure.", gotException); - innerScope.close(); - tracer.close(); - } - - /** - * Test calling detach() two times on a scope object. - */ - @Test - public void TestDoubleDetachIsCaught() throws Exception { - Tracer tracer = new Tracer.Builder(). - name("TestDoubleDetach"). - tracerPool(new TracerPool("TestDoubleDetachIsCaught")). - conf(HTraceConfiguration. - fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build(); - boolean gotException = false; - TraceScope myScope = tracer.newScope("myScope"); - myScope.detach(); - try { - myScope.detach(); - } catch (RuntimeException e) { - assertThat(e.getMessage(), - containsString("it is already detached.")); - gotException = true; - } - assertTrue("Expected to get exception because of double TraceScope " + - "detach.", gotException); - tracer.close(); - } - - /** - * Test calling detach() two times on a scope object. - */ - @Test - public void TestDoubleDetachOnNullScope() throws Exception { - Tracer tracer = new Tracer.Builder(). - name("TestDoubleDetachOnNullScope"). - tracerPool(new TracerPool("TestDoubleDetachOnNullScope")). - conf(HTraceConfiguration. - fromKeyValuePairs("sampler.classes", "NeverSampler")).build(); - boolean gotException = false; - TraceScope myScope = tracer.newScope("myScope"); - myScope.detach(); - try { - myScope.detach(); - } catch (RuntimeException e) { - assertThat(e.getMessage(), - containsString("it is already detached.")); - gotException = true; - } - assertTrue("Expected to get exception because of double TraceScope " + - "detach on NullScope.", gotException); - tracer.close(); - } - - /** - * Test calling reattach() two times on a scope object. - */ - @Test - public void TestDoubleReattachIsCaught() throws Exception { - Tracer tracer = new Tracer.Builder(). - name("TestDoubleReattach"). - tracerPool(new TracerPool("TestDoubleReattachIsCaught")). - conf(HTraceConfiguration. - fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build(); - boolean gotException = false; - TraceScope myScope = tracer.newScope("myScope"); - myScope.detach(); - myScope.reattach(); - try { - myScope.reattach(); - } catch (RuntimeException e) { - assertThat(e.getMessage(), - containsString("it is not detached.")); - gotException = true; - } - assertTrue("Expected to get exception because of double TraceScope " + - "reattach.", gotException); - tracer.close(); - } - - private static class ScopeHolder { - TraceScope scope; - - void set(TraceScope scope) { - this.scope = scope; - } - } - - /** - * Test correctly passing spans between threads using detach(). - */ - @Test - public void TestPassingSpanBetweenThreads() throws Exception { - final Tracer tracer = new Tracer.Builder(). - name("TestPassingSpanBetweenThreads"). - tracerPool(new TracerPool("TestPassingSpanBetweenThreads")). - conf(HTraceConfiguration. - fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build(); - POJOSpanReceiver receiver = - new POJOSpanReceiver(HTraceConfiguration.EMPTY); - tracer.getTracerPool().addReceiver(receiver); - final ScopeHolder scopeHolder = new ScopeHolder(); - Thread th = new Thread(new Runnable() { - @Override - public void run() { - TraceScope workerScope = tracer.newScope("workerSpan"); - workerScope.detach(); - scopeHolder.set(workerScope); - } - }); - th.start(); - th.join(); - TraceScope workerScope = scopeHolder.scope; - SpanId workerScopeId = workerScope.getSpan().getSpanId(); - - // Create new scope whose parent is the worker thread's span. - workerScope.reattach(); - TraceScope nested = tracer.newScope("nested"); - nested.close(); - // Create another span which also descends from the worker thread's span. - TraceScope nested2 = tracer.newScope("nested2"); - nested2.close(); - - // Close the worker thread's span. - workerScope.close(); - - // We can create another descendant, even though the worker thread's span - // has been stopped. - TraceScope lateChildScope = tracer.newScope("lateChild", workerScopeId); - lateChildScope.close(); - tracer.close(); - - TraceGraph traceGraph = new TraceGraph(receiver.getSpans()); - Collection rootSpans = - traceGraph.getSpansByParent().find(SpanId.INVALID); - Assert.assertEquals(1, rootSpans.size()); - Assert.assertEquals(workerScopeId, - rootSpans.iterator().next().getSpanId()); - Collection childSpans = - traceGraph.getSpansByParent().find(workerScopeId); - Assert.assertEquals(3, childSpans.size()); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestCountSampler.java b/htrace-core/src/test/java/org/apache/htrace/core/TestCountSampler.java deleted file mode 100644 index e26115d..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TestCountSampler.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import org.junit.Assert; -import org.junit.Test; - -public class TestCountSampler { - - @Test - public void testNext() { - CountSampler half = new CountSampler(HTraceConfiguration. - fromKeyValuePairs("sampler.frequency", "2")); - CountSampler hundred = new CountSampler(HTraceConfiguration. - fromKeyValuePairs("sampler.frequency", "100")); - int halfCount = 0; - int hundredCount = 0; - for (int i = 0; i < 200; i++) { - if (half.next()) - halfCount++; - if (hundred.next()) - hundredCount++; - } - Assert.assertEquals(2, hundredCount); - Assert.assertEquals(100, halfCount); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestHTrace.java b/htrace-core/src/test/java/org/apache/htrace/core/TestHTrace.java deleted file mode 100644 index 06ca189..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TestHTrace.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -import org.apache.htrace.core.TraceGraph.SpansByParent; - -import org.junit.Assert; -import org.junit.Test; - -public class TestHTrace { - @Test - public void TestTracerCreateAndClose() throws Exception { - Tracer tracer = new Tracer.Builder(). - name("TestSimpleScope"). - tracerPool(new TracerPool("TestTracerCreateAndClose")). - conf(HTraceConfiguration.fromKeyValuePairs( - "sampler.classes", "AlwaysSampler")). - build(); - POJOSpanReceiver receiver = - new POJOSpanReceiver(HTraceConfiguration.EMPTY); - tracer.getTracerPool().addReceiver(receiver); - tracer.close(); - Assert.assertTrue(receiver.getSpans().isEmpty()); - } - - @Test - public void TestSimpleScope() throws Exception { - Tracer tracer = new Tracer.Builder(). - name("TestSimpleScope"). - tracerPool(new TracerPool("TestSimpleScope")). - conf(HTraceConfiguration.fromKeyValuePairs( - "sampler.classes", "AlwaysSampler")). - build(); - POJOSpanReceiver receiver = - new POJOSpanReceiver(HTraceConfiguration.EMPTY); - tracer.getTracerPool().addReceiver(receiver); - TraceScope scope = tracer.newScope("Foo"); - scope.close(); - tracer.close(); - Assert.assertEquals(1, receiver.getSpans().size()); - Span span = receiver.getSpans().iterator().next(); - Assert.assertEquals(0, span.getParents().length); - } - - @Test - public void TestCreateSpans() throws Exception { - Tracer tracer = new Tracer.Builder(). - name("TestCreateSpans"). - tracerPool(new TracerPool("TestCreateSpans")). - conf(HTraceConfiguration.fromKeyValuePairs( - "sampler.classes", "AlwaysSampler")). - build(); - POJOSpanReceiver receiver = - new POJOSpanReceiver(HTraceConfiguration.EMPTY); - tracer.getTracerPool().addReceiver(receiver); - TraceCreator traceCreator = new TraceCreator(tracer); - traceCreator.createSampleRpcTrace(); - traceCreator.createSimpleTrace(); - traceCreator.createThreadedTrace(); - tracer.close(); - TraceGraph traceGraph = new TraceGraph(receiver.getSpans()); - Collection roots = traceGraph.getSpansByParent().find(SpanId.INVALID); - Assert.assertTrue("Trace tree must have roots", !roots.isEmpty()); - Assert.assertEquals(3, roots.size()); - - Map descriptionToRootSpan = new HashMap(); - for (Span root : roots) { - descriptionToRootSpan.put(root.getDescription(), root); - } - - Assert.assertTrue(descriptionToRootSpan.keySet().contains( - TraceCreator.RPC_TRACE_ROOT)); - Assert.assertTrue(descriptionToRootSpan.keySet().contains( - TraceCreator.SIMPLE_TRACE_ROOT)); - Assert.assertTrue(descriptionToRootSpan.keySet().contains( - TraceCreator.THREADED_TRACE_ROOT)); - - SpansByParent spansByParentId = traceGraph.getSpansByParent(); - - Span rpcTraceRoot = descriptionToRootSpan.get(TraceCreator.RPC_TRACE_ROOT); - Assert.assertEquals(1, spansByParentId.find(rpcTraceRoot.getSpanId()).size()); - - Span rpcTraceChild1 = spansByParentId.find(rpcTraceRoot.getSpanId()) - .iterator().next(); - Assert.assertEquals(1, spansByParentId.find(rpcTraceChild1.getSpanId()).size()); - - Span rpcTraceChild2 = spansByParentId.find(rpcTraceChild1.getSpanId()) - .iterator().next(); - Assert.assertEquals(1, spansByParentId.find(rpcTraceChild2.getSpanId()).size()); - - Span rpcTraceChild3 = spansByParentId.find(rpcTraceChild2.getSpanId()) - .iterator().next(); - Assert.assertEquals(0, spansByParentId.find(rpcTraceChild3.getSpanId()).size()); - } - - @Test(timeout=60000) - public void testRootSpansHaveNonZeroSpanId() throws Exception { - Tracer tracer = new Tracer.Builder(). - name("testRootSpansHaveNonZeroSpanId"). - tracerPool(new TracerPool("testRootSpansHaveNonZeroSpanId")). - conf(HTraceConfiguration.fromKeyValuePairs( - "sampler.classes", "AlwaysSampler")).build(); - TraceScope scope = tracer. - newScope("myRootSpan", new SpanId(100L, 200L)); - Assert.assertNotNull(scope); - Assert.assertEquals("myRootSpan", scope.getSpan().getDescription()); - Assert.assertTrue(scope.getSpan().getSpanId().isValid()); - Assert.assertEquals(100L, scope.getSpan().getSpanId().getHigh()); - Assert.assertNotEquals(0L, scope.getSpan().getSpanId().getLow()); - scope.close(); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java b/htrace-core/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java deleted file mode 100644 index 7ca897f..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; - -public class TestHTraceConfiguration { - @Test - public void testGetBoolean() throws Exception { - - Map m = new HashMap(); - m.put("testTrue", " True"); - m.put("testFalse", "falsE "); - HTraceConfiguration configuration = HTraceConfiguration.fromMap(m); - - // Tests for value being there - assertTrue(configuration.getBoolean("testTrue", false)); - assertFalse(configuration.getBoolean("testFalse", true)); - - // Test for absent - assertTrue(configuration.getBoolean("absent", true)); - assertFalse(configuration.getBoolean("absent", false)); - } - - @Test - public void testGetInt() throws Exception { - Map m = new HashMap(); - m.put("a", "100"); - m.put("b", "0"); - m.put("c", "-100"); - m.put("d", "5"); - - HTraceConfiguration configuration = HTraceConfiguration.fromMap(m); - assertEquals(100, configuration.getInt("a", -999)); - assertEquals(0, configuration.getInt("b", -999)); - assertEquals(-100, configuration.getInt("c", -999)); - assertEquals(5, configuration.getInt("d", -999)); - assertEquals(-999, configuration.getInt("absent", -999)); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java b/htrace-core/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java deleted file mode 100644 index 9388707..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.IOException; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.Test; - -public class TestLocalFileSpanReceiver { - @Test - public void testUniqueLocalTraceFileName() { - String filename1 = LocalFileSpanReceiver.getUniqueLocalTraceFileName(); - String filename2 = LocalFileSpanReceiver.getUniqueLocalTraceFileName(); - boolean eq = filename1.equals(filename2); - if (System.getProperty("os.name").startsWith("Linux")) { - // ${java.io.tmpdir}/[pid] - assertTrue(eq); - } else { - // ${java.io.tmpdir}/[random UUID] - assertFalse(eq); - } - } - - @Test - public void testWriteToLocalFile() throws IOException { - String traceFileName = LocalFileSpanReceiver.getUniqueLocalTraceFileName(); - Tracer tracer = new Tracer.Builder(). - name("testWriteToLocalFileTracer"). - tracerPool(new TracerPool("testWriteToLocalFile")). - conf(HTraceConfiguration.fromKeyValuePairs( - "sampler.classes", "AlwaysSampler", - "span.receiver.classes", LocalFileSpanReceiver.class.getName(), - "local.file.span.receiver.path", traceFileName, - "tracer.id", "%{tname}")). - build(); - TraceScope scope = tracer.newScope("testWriteToLocalFile"); - scope.close(); - tracer.close(); - - ObjectMapper mapper = new ObjectMapper(); - MilliSpan span = mapper.readValue(new File(traceFileName), MilliSpan.class); - assertEquals("testWriteToLocalFile", span.getDescription()); - assertEquals("testWriteToLocalFileTracer", span.getTracerId()); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestMilliSpan.java b/htrace-core/src/test/java/org/apache/htrace/core/TestMilliSpan.java deleted file mode 100644 index 7ce1fdb..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TestMilliSpan.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; - -public class TestMilliSpan { - private void compareSpans(Span expected, Span got) throws Exception { - assertEquals(expected.getStartTimeMillis(), got.getStartTimeMillis()); - assertEquals(expected.getStopTimeMillis(), got.getStopTimeMillis()); - assertEquals(expected.getDescription(), got.getDescription()); - assertEquals(expected.getSpanId(), got.getSpanId()); - assertEquals(expected.getTracerId(), got.getTracerId()); - assertTrue(Arrays.equals(expected.getParents(), got.getParents())); - Map expectedT = expected.getKVAnnotations(); - Map gotT = got.getKVAnnotations(); - if (expectedT == null) { - assertEquals(null, gotT); - } else { - assertEquals(expectedT.size(), gotT.size()); - for (String key : expectedT.keySet()) { - assertEquals(expectedT.get(key), gotT.get(key)); - } - } - List expectedTimeline = - expected.getTimelineAnnotations(); - List gotTimeline = - got.getTimelineAnnotations(); - if (expectedTimeline == null) { - assertEquals(null, gotTimeline); - } else { - assertEquals(expectedTimeline.size(), gotTimeline.size()); - Iterator iter = gotTimeline.iterator(); - for (TimelineAnnotation expectedAnn : expectedTimeline) { - TimelineAnnotation gotAnn = iter.next(); - assertEquals(expectedAnn.getMessage(), gotAnn.getMessage()); - assertEquals(expectedAnn.getTime(), gotAnn.getTime()); - } - } - } - - @Test - public void testJsonSerialization() throws Exception { - MilliSpan span = new MilliSpan.Builder(). - description("foospan"). - begin(123L). - end(456L). - parents(new SpanId[] { new SpanId(7L, 7L) }). - tracerId("b2404.halxg.com:8080"). - spanId(new SpanId(7L, 8L)). - build(); - String json = span.toJson(); - MilliSpan dspan = MilliSpan.fromJson(json); - compareSpans(span, dspan); - } - - @Test - public void testJsonSerializationWithNegativeLongValue() throws Exception { - MilliSpan span = new MilliSpan.Builder(). - description("foospan"). - begin(-1L). - end(-1L). - parents(new SpanId[] { new SpanId(-1L, -1L) }). - tracerId("b2404.halxg.com:8080"). - spanId(new SpanId(-1L, -2L)). - build(); - String json = span.toJson(); - MilliSpan dspan = MilliSpan.fromJson(json); - compareSpans(span, dspan); - } - - @Test - public void testJsonSerializationWithRandomLongValue() throws Exception { - SpanId parentId = SpanId.fromRandom(); - MilliSpan span = new MilliSpan.Builder(). - description("foospan"). - begin(ThreadLocalRandom.current().nextLong()). - end(ThreadLocalRandom.current().nextLong()). - parents(new SpanId[] { parentId }). - tracerId("b2404.halxg.com:8080"). - spanId(parentId.newChildId()). - build(); - String json = span.toJson(); - MilliSpan dspan = MilliSpan.fromJson(json); - compareSpans(span, dspan); - } - - @Test - public void testJsonSerializationWithOptionalFields() throws Exception { - MilliSpan.Builder builder = new MilliSpan.Builder(). - description("foospan"). - begin(300). - end(400). - parents(new SpanId[] { }). - tracerId("b2408.halxg.com:8080"). - spanId(new SpanId(111111111L, 111111111L)); - Map traceInfo = new HashMap(); - traceInfo.put("abc", "123"); - traceInfo.put("def", "456"); - builder.traceInfo(traceInfo); - List timeline = new LinkedList(); - timeline.add(new TimelineAnnotation(310L, "something happened")); - timeline.add(new TimelineAnnotation(380L, "something else happened")); - timeline.add(new TimelineAnnotation(390L, "more things")); - builder.timeline(timeline); - MilliSpan span = builder.build(); - String json = span.toJson(); - MilliSpan dspan = MilliSpan.fromJson(json); - compareSpans(span, dspan); - } - - @Test - public void testJsonSerializationWithFieldsNotSet() throws Exception { - MilliSpan span = new MilliSpan.Builder().build(); - String json = span.toJson(); - MilliSpan dspan = MilliSpan.fromJson(json); - compareSpans(span, dspan); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestNullScope.java b/htrace-core/src/test/java/org/apache/htrace/core/TestNullScope.java deleted file mode 100644 index c8ed7f1..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TestNullScope.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import org.junit.Assert; -import org.junit.Test; - -public class TestNullScope { - private void verifyNullScope(TraceScope scope) { - Assert.assertTrue(null == scope.getSpan()); - Assert.assertFalse(scope.detached); - scope.detach(); - Assert.assertTrue(scope.detached); - scope.reattach(); - Assert.assertFalse(scope.detached); - scope.close(); - } - - @Test - public void testNullScope() { - Tracer tracer = new Tracer.Builder(). - name("testNullScope"). - tracerPool(new TracerPool("testNullScope")). - conf(HTraceConfiguration.EMPTY). - build(); - verifyNullScope(tracer.newScope("testNullScope")); - verifyNullScope(tracer.newNullScope()); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestSampler.java b/htrace-core/src/test/java/org/apache/htrace/core/TestSampler.java deleted file mode 100644 index 2305d9f..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TestSampler.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -public class TestSampler { - private Sampler[] getSamplersFromConf(HTraceConfiguration conf) { - Tracer tracer = new Tracer.Builder(). - name("MyTracer"). - tracerPool(new TracerPool("getSamplersFromConf")). - conf(conf). - build(); - Sampler[] samplers = tracer.getSamplers(); - tracer.close(); - return samplers; - } - - private void checkArrayContains(List> expected, - Sampler[] samplers) { - for (Iterator> iter = expected.iterator(); - iter.hasNext(); ) { - Class samplerClass = iter.next(); - boolean found = false; - for (int i = 0; i < samplers.length; i++) { - if (samplers[i] != null) { - if (samplers[i].getClass().equals(samplerClass)) { - samplers[i] = null; - found = true; - break; - } - } - } - Assert.assertTrue("Failed to find sampler class " + - samplerClass.getName(), found); - } - for (int i = 0; i < samplers.length; i++) { - if (samplers[i] != null) { - Assert.fail("Got extra sampler of type " + - samplers.getClass().getName()); - } - } - } - - private void checkArrayContains(Class expected, Sampler[] samplers) { - LinkedList> expectedList = - new LinkedList>(); - expectedList.add(expected); - checkArrayContains(expectedList, samplers); - } - - @Test - public void testTracerBuilderCreatesCorrectSamplers() { - Sampler[] samplers = getSamplersFromConf(HTraceConfiguration. - fromKeyValuePairs("sampler.classes", "AlwaysSampler")); - checkArrayContains(AlwaysSampler.class, samplers); - - samplers = getSamplersFromConf(HTraceConfiguration. - fromKeyValuePairs("sampler.classes", "NeverSampler")); - checkArrayContains(NeverSampler.class, samplers); - - samplers = getSamplersFromConf(HTraceConfiguration. - fromKeyValuePairs("sampler.classes", "NonExistentSampler")); - Assert.assertEquals(0, samplers.length); - - samplers = getSamplersFromConf(HTraceConfiguration.EMPTY); - Assert.assertEquals(0, samplers.length); - } - - @Test - public void testAlwaysSampler() { - AlwaysSampler sampler = new AlwaysSampler(HTraceConfiguration.EMPTY); - Assert.assertTrue(sampler.next()); - } - - @Test - public void testNeverSampler() { - NeverSampler sampler = new NeverSampler(HTraceConfiguration.EMPTY); - Assert.assertTrue(!sampler.next()); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestSpanId.java b/htrace-core/src/test/java/org/apache/htrace/core/TestSpanId.java deleted file mode 100644 index bb57368..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TestSpanId.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.util.Random; - -import org.junit.Assert; -import org.junit.Test; - -public class TestSpanId { - private void testRoundTrip(SpanId id) throws Exception { - String str = id.toString(); - SpanId id2 = SpanId.fromString(str); - Assert.assertEquals(id, id2); - } - - @Test - public void testToStringAndFromString() throws Exception { - testRoundTrip(SpanId.INVALID); - testRoundTrip(new SpanId(0x1234567812345678L, 0x1234567812345678L)); - testRoundTrip(new SpanId(0xf234567812345678L, 0xf234567812345678L)); - testRoundTrip(new SpanId(0xffffffffffffffffL, 0xffffffffffffffffL)); - Random rand = new Random(12345); - for (int i = 0; i < 100; i++) { - testRoundTrip(new SpanId(rand.nextLong(), rand.nextLong())); - } - } - - @Test - public void testValidAndInvalidIds() throws Exception { - Assert.assertFalse(SpanId.INVALID.isValid()); - Assert.assertTrue( - new SpanId(0x1234567812345678L, 0x1234567812345678L).isValid()); - Assert.assertTrue( - new SpanId(0xf234567812345678L, 0xf234567812345678L).isValid()); - } - - private void expectLessThan(SpanId a, SpanId b) throws Exception { - int cmp = a.compareTo(b); - Assert.assertTrue("Expected " + a + " to be less than " + b, - (cmp < 0)); - int cmp2 = b.compareTo(a); - Assert.assertTrue("Expected " + b + " to be greater than " + a, - (cmp2 > 0)); - } - - @Test - public void testIdComparisons() throws Exception { - expectLessThan(new SpanId(0x0000000000000001L, 0x0000000000000001L), - new SpanId(0x0000000000000001L, 0x0000000000000002L)); - expectLessThan(new SpanId(0x0000000000000001L, 0x0000000000000001L), - new SpanId(0x0000000000000002L, 0x0000000000000000L)); - expectLessThan(SpanId.INVALID, - new SpanId(0xffffffffffffffffL, 0xffffffffffffffffL)); - expectLessThan(new SpanId(0x1234567812345678L, 0x1234567812345678L), - new SpanId(0x1234567812345678L, 0xf234567812345678L)); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java b/htrace-core/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java deleted file mode 100644 index b97d624..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.Assert; -import org.junit.Test; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -public class TestSpanReceiverBuilder { - private static final Log LOG = - LogFactory.getLog(TestSpanReceiverBuilder.class); - - private List createSpanReceivers(String classes) { - Tracer tracer = new Tracer.Builder(). - name("MyTracer"). - tracerPool(new TracerPool("createSpanReceivers")). - conf(HTraceConfiguration.fromKeyValuePairs( - "span.receiver.classes", classes)). - build(); - SpanReceiver[] receivers = tracer.getTracerPool().getReceivers(); - tracer.close(); - LinkedList receiverList = new LinkedList(); - for (SpanReceiver item: receivers) { - receiverList.add(item); - } - return receiverList; - } - - @Test - public void TestCreateStandardSpanReceivers() { - List receivers; - receivers = createSpanReceivers(""); - Assert.assertTrue(receivers.isEmpty()); - receivers = createSpanReceivers("POJOSpanReceiver"); - Assert.assertTrue(receivers.get(0).getClass().getName(). - equals("org.apache.htrace.core.POJOSpanReceiver")); - receivers = createSpanReceivers( - "org.apache.htrace.core.StandardOutSpanReceiver"); - Assert.assertTrue(receivers.get(0).getClass().getName(). - equals("org.apache.htrace.core.StandardOutSpanReceiver")); - receivers = createSpanReceivers( - "POJOSpanReceiver;StandardOutSpanReceiver"); - Assert.assertEquals(2, receivers.size()); - for (Iterator iter = receivers.iterator(); iter.hasNext();) { - SpanReceiver receiver = iter.next(); - if (receiver.getClass().getName().equals( - "org.apache.htrace.core.POJOSpanReceiver")) { - iter.remove(); - break; - } - } - for (Iterator iter = receivers.iterator(); iter.hasNext();) { - SpanReceiver receiver = iter.next(); - if (receiver.getClass().getName().equals( - "org.apache.htrace.core.StandardOutSpanReceiver")) { - iter.remove(); - break; - } - } - Assert.assertEquals(0, receivers.size()); - } - - public static class GoodSpanReceiver extends SpanReceiver { - public GoodSpanReceiver(HTraceConfiguration conf) { - } - - @Override - public void receiveSpan(Span span) { - } - - @Override - public void close() throws IOException { - } - } - - public static class BadSpanReceiver extends SpanReceiver { - public BadSpanReceiver(HTraceConfiguration conf) { - throw new RuntimeException("Can't create BadSpanReceiver"); - } - - @Override - public void receiveSpan(Span span) { - } - - @Override - public void close() throws IOException { - } - } - - /** - * Test trying to create a SpanReceiver that experiences an error in the - * constructor. - */ - @Test - public void testGetSpanReceiverWithConstructorError() throws Exception { - List receivers; - receivers = createSpanReceivers( - GoodSpanReceiver.class.getName()); - Assert.assertEquals(1, receivers.size()); - Assert.assertTrue(receivers.get(0).getClass().getName(). - contains("GoodSpanReceiver")); - receivers = createSpanReceivers( - BadSpanReceiver.class.getName()); - Assert.assertEquals(0, receivers.size()); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestTracerId.java b/htrace-core/src/test/java/org/apache/htrace/core/TestTracerId.java deleted file mode 100644 index 1e842c5..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TestTracerId.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import static org.junit.Assert.assertEquals; - -import java.io.IOException; - -import org.junit.Test; - -public class TestTracerId { - private void testTracerIdImpl(String expected, String fmt) { - assertEquals(expected, new TracerId( - HTraceConfiguration.fromKeyValuePairs(TracerId.TRACER_ID_KEY, fmt), - "TracerName").get()); - } - - @Test - public void testSimpleTracerIds() { - testTracerIdImpl("abc", "abc"); - testTracerIdImpl("abc", "a\\bc"); - testTracerIdImpl("abc", "ab\\c"); - testTracerIdImpl("abc", "\\a\\b\\c"); - testTracerIdImpl("a\\bc", "a\\\\bc"); - } - - @Test - public void testSubstitutionVariables() throws IOException { - testTracerIdImpl("myTracerName", "my%{tname}"); - testTracerIdImpl(TracerId.getProcessName(), "%{pname}"); - testTracerIdImpl("my." + TracerId.getProcessName(), "my.%{pname}"); - testTracerIdImpl(TracerId.getBestIpString() + ".str", "%{ip}.str"); - testTracerIdImpl("%{pname}", "\\%{pname}"); - testTracerIdImpl("%cash%money{}", "%cash%money{}"); - testTracerIdImpl("Foo." + Long.valueOf(TracerId.getOsPid()).toString(), - "Foo.%{pid}"); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TraceCreator.java b/htrace-core/src/test/java/org/apache/htrace/core/TraceCreator.java deleted file mode 100644 index b843999..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TraceCreator.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; - -/** - * Does some stuff and traces it. - */ -public class TraceCreator { - public static final String RPC_TRACE_ROOT = "createSampleRpcTrace"; - public static final String THREADED_TRACE_ROOT = "createThreadedTrace"; - public static final String SIMPLE_TRACE_ROOT = "createSimpleTrace"; - - private final Tracer tracer; - - public TraceCreator(Tracer tracer) { - this.tracer = tracer; - } - - public void createSampleRpcTrace() { - TraceScope s = tracer.newScope(RPC_TRACE_ROOT); - try { - pretendRpcSend(); - } finally { - s.close(); - } - } - - public void createSimpleTrace() { - TraceScope s = tracer.newScope(SIMPLE_TRACE_ROOT); - try { - importantWork1(); - } finally { - s.close(); - } - } - - /** - * Creates the demo trace (will create different traces from call to call). - */ - public void createThreadedTrace() { - TraceScope s = tracer.newScope(THREADED_TRACE_ROOT); - try { - Random r = ThreadLocalRandom.current(); - int numThreads = r.nextInt(4) + 1; - Thread[] threads = new Thread[numThreads]; - - for (int i = 0; i < numThreads; i++) { - threads[i] = new Thread(tracer.wrap(new MyRunnable(), null)); - } - for (int i = 0; i < numThreads; i++) { - threads[i].start(); - } - for (int i = 0; i < numThreads; i++) { - try { - threads[i].join(); - } catch (InterruptedException e) { - } - } - importantWork1(); - } finally { - s.close(); - } - } - - private void importantWork1() { - TraceScope cur = tracer.newScope("important work 1"); - try { - Thread.sleep((long) (2000 * Math.random())); - importantWork2(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } finally { - cur.close(); - } - } - - private void importantWork2() { - TraceScope cur = tracer.newScope("important work 2"); - try { - Thread.sleep((long) (2000 * Math.random())); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } finally { - cur.close(); - } - } - - private class MyRunnable implements Runnable { - @Override - public void run() { - try { - Thread.sleep(750); - Random r = ThreadLocalRandom.current(); - int importantNumber = 100 / r.nextInt(3); - System.out.println("Important number: " + importantNumber); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - } catch (ArithmeticException ae) { - TraceScope c = tracer.newScope("dealing with arithmetic exception."); - try { - Thread.sleep((long) (3000 * Math.random())); - } catch (InterruptedException ie1) { - Thread.currentThread().interrupt(); - } finally { - c.close(); - } - } - } - } - - public void pretendRpcSend() { - Span span = tracer.getCurrentSpan(); - pretendRpcReceiveWithTraceInfo(span.getSpanId()); - } - - public void pretendRpcReceiveWithTraceInfo(SpanId parentId) { - TraceScope s = tracer.newScope("received RPC", parentId); - try { - importantWork1(); - } finally { - s.close(); - } - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TraceGraph.java b/htrace-core/src/test/java/org/apache/htrace/core/TraceGraph.java deleted file mode 100644 index a06e620..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/core/TraceGraph.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.core; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.TreeSet; - -/** - * Used to create the graph formed by spans. - */ -public class TraceGraph { - private static final Log LOG = LogFactory.getLog(Tracer.class); - - - public static class SpansByParent { - /** - * Compare two spans by span ID. - */ - private static Comparator COMPARATOR = - new Comparator() { - @Override - public int compare(Span a, Span b) { - return a.getSpanId().compareTo(b.getSpanId()); - } - }; - - private final TreeSet treeSet; - - private final HashMap> parentToSpans; - - SpansByParent(Collection spans) { - TreeSet treeSet = new TreeSet(COMPARATOR); - parentToSpans = new HashMap>(); - for (Span span : spans) { - treeSet.add(span); - for (SpanId parent : span.getParents()) { - LinkedList list = parentToSpans.get(parent); - if (list == null) { - list = new LinkedList(); - parentToSpans.put(parent, list); - } - list.add(span); - } - if (span.getParents().length == 0) { - LinkedList list = parentToSpans.get(SpanId.INVALID); - if (list == null) { - list = new LinkedList(); - parentToSpans.put(SpanId.INVALID, list); - } - list.add(span); - } - } - this.treeSet = treeSet; - } - - public List find(SpanId parentId) { - LinkedList spans = parentToSpans.get(parentId); - if (spans == null) { - return new LinkedList(); - } - return spans; - } - - public Iterator iterator() { - return Collections.unmodifiableSortedSet(treeSet).iterator(); - } - } - - public static class SpansByTracerId { - /** - * Compare two spans by process ID, and then by span ID. - */ - private static Comparator COMPARATOR = - new Comparator() { - @Override - public int compare(Span a, Span b) { - int cmp = a.getTracerId().compareTo(b.getTracerId()); - if (cmp != 0) { - return cmp; - } - return a.getSpanId().compareTo(b.getSpanId()); - } - }; - - private final TreeSet treeSet; - - SpansByTracerId(Collection spans) { - TreeSet treeSet = new TreeSet(COMPARATOR); - for (Span span : spans) { - treeSet.add(span); - } - this.treeSet = treeSet; - } - - public List find(String tracerId) { - List spans = new ArrayList(); - Span span = new MilliSpan.Builder(). - spanId(SpanId.INVALID). - tracerId(tracerId). - build(); - while (true) { - span = treeSet.higher(span); - if (span == null) { - break; - } - if (span.getTracerId().equals(tracerId)) { - break; - } - spans.add(span); - } - return spans; - } - - public Iterator iterator() { - return Collections.unmodifiableSortedSet(treeSet).iterator(); - } - } - - private final SpansByParent spansByParent; - private final SpansByTracerId spansByTracerId; - - /** - * Create a new TraceGraph - * - * @param spans The collection of spans to use to create this TraceGraph. Should - * have at least one root span. - */ - public TraceGraph(Collection spans) { - this.spansByParent = new SpansByParent(spans); - this.spansByTracerId = new SpansByTracerId(spans); - } - - public SpansByParent getSpansByParent() { - return spansByParent; - } - - public SpansByTracerId getSpansByTracerId() { - return spansByTracerId; - } - - @Override - public String toString() { - StringBuilder bld = new StringBuilder(); - String prefix = ""; - for (Iterator iter = spansByParent.iterator(); iter.hasNext();) { - Span span = iter.next(); - bld.append(prefix).append(span.toString()); - prefix = "\n"; - } - return bld.toString(); - } -} diff --git a/htrace-core/src/test/java/org/apache/htrace/util/TestUtil.java b/htrace-core/src/test/java/org/apache/htrace/util/TestUtil.java deleted file mode 100644 index 7cb4aed..0000000 --- a/htrace-core/src/test/java/org/apache/htrace/util/TestUtil.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.htrace.util; - -import java.io.File; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * Utilities for writing unit tests. - */ -public class TestUtil { - /** - * Get a dump of the stack traces of all threads. - */ - public static String threadDump() { - StringBuilder dump = new StringBuilder(); - Map stackTraces = Thread.getAllStackTraces(); - for (Map.Entry e : stackTraces.entrySet()) { - Thread thread = e.getKey(); - dump.append(String.format( - "\"%s\" %s prio=%d tid=%d %s\njava.lang.Thread.State: %s", - thread.getName(), - (thread.isDaemon() ? "daemon" : ""), - thread.getPriority(), - thread.getId(), - Thread.State.WAITING.equals(thread.getState()) ? - "in Object.wait()" : thread.getState().name().toLowerCase(), - Thread.State.WAITING.equals(thread.getState()) ? - "WAITING (on object monitor)" : thread.getState())); - for (StackTraceElement stackTraceElement : e.getValue()) { - dump.append("\n at "); - dump.append(stackTraceElement); - } - dump.append("\n"); - } - return dump.toString(); - } - - /** - * A callback which returns a value of type T. - * - * TODO: remove this when we're on Java 8, in favor of - * java.util.function.Supplier. - */ - public interface Supplier { - T get(); - } - - /** - * Wait for a condition to become true for a configurable amount of time. - * - * @param check The condition to wait for. - * @param periodMs How often to check the condition, in milliseconds. - * @param timeoutMs How long to wait in total, in milliseconds. - */ - public static void waitFor(Supplier check, - long periodMs, long timeoutMs) - throws TimeoutException, InterruptedException - { - long endNs = System.nanoTime() + - TimeUnit.NANOSECONDS.convert(timeoutMs, TimeUnit.MILLISECONDS); - while (true) { - boolean result = check.get(); - if (result) { - return; - } - long nowNs = System.nanoTime(); - if (nowNs >= endNs) { - throw new TimeoutException("Timed out waiting for test condition. " + - "Thread dump:\n" + threadDump()); - } - Thread.sleep(periodMs); - } - } -} diff --git a/htrace-core4/pom.xml b/htrace-core4/pom.xml new file mode 100644 index 0000000..81f2f87 --- /dev/null +++ b/htrace-core4/pom.xml @@ -0,0 +1,125 @@ + + + + 4.0.0 + + htrace-core4 + jar + + + htrace + org.apache.htrace + 4.1.0-incubating-SNAPSHOT + .. + + + htrace-core + http://incubator.apache.org/projects/htrace.html + + + UTF-8 + + + + + + org.apache.maven.plugins + maven-source-plugin + + + maven-javadoc-plugin + + + maven-compiler-plugin + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + + + org.apache.commons.logging + org.apache.htrace.commons.logging + + + com.fasterxml.jackson + org.apache.htrace.fasterxml.jackson + + + + + shade + + + + + + org.apache.maven.plugins + maven-gpg-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.rat + apache-rat-plugin + + + + maven-deploy-plugin + + + + + + + + junit + junit + test + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + + commons-logging + commons-logging + + + + + + dist + + + + + maven-assembly-plugin + + true + + + + + + + diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/AlwaysSampler.java b/htrace-core4/src/main/java/org/apache/htrace/core/AlwaysSampler.java new file mode 100644 index 0000000..8d5a296 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/AlwaysSampler.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +/** + * A Sampler that always returns true. + */ +public final class AlwaysSampler extends Sampler { + public static final AlwaysSampler INSTANCE = new AlwaysSampler(null); + + public AlwaysSampler(HTraceConfiguration conf) { + } + + @Override + public boolean next() { + return true; + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/CountSampler.java b/htrace-core4/src/main/java/org/apache/htrace/core/CountSampler.java new file mode 100644 index 0000000..5a838c7 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/CountSampler.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.util.concurrent.ThreadLocalRandom; + +/** + * Sampler that returns true every N calls. Specify the frequency interval by configuring a + * {@code long} value for {@link #SAMPLER_FREQUENCY_CONF_KEY}. + */ +public class CountSampler extends Sampler { + public final static String SAMPLER_FREQUENCY_CONF_KEY = "sampler.frequency"; + + final long frequency; + long count = ThreadLocalRandom.current().nextLong(); + + public CountSampler(HTraceConfiguration conf) { + this.frequency = Long.parseLong(conf.get(SAMPLER_FREQUENCY_CONF_KEY), 10); + } + + @Override + public boolean next() { + return (count++ % frequency) == 0; + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/HTraceConfiguration.java b/htrace-core4/src/main/java/org/apache/htrace/core/HTraceConfiguration.java new file mode 100644 index 0000000..c6e445b --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/HTraceConfiguration.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.HashMap; +import java.util.Map; + +/** + * Wrapper which integrating applications should implement in order + * to provide tracing configuration. + */ +public abstract class HTraceConfiguration { + + private static final Log LOG = LogFactory.getLog(HTraceConfiguration.class); + + private static final Map EMPTY_MAP = new HashMap(1); + + /** + * An empty HTrace configuration. + */ + public static final HTraceConfiguration EMPTY = fromMap(EMPTY_MAP); + + /** + * Create an HTrace configuration from a map. + * + * @param conf The map to create the configuration from. + * @return The new configuration. + */ + public static HTraceConfiguration fromMap(Map conf) { + return new MapConf(conf); + } + + public static HTraceConfiguration fromKeyValuePairs(String... pairs) { + if ((pairs.length % 2) != 0) { + throw new RuntimeException("You must specify an equal number of keys " + + "and values."); + } + Map conf = new HashMap(); + for (int i = 0; i < pairs.length; i+=2) { + conf.put(pairs[i], pairs[i + 1]); + } + return new MapConf(conf); + } + + public abstract String get(String key); + + public abstract String get(String key, String defaultValue); + + public boolean getBoolean(String key, boolean defaultValue) { + String value = get(key, String.valueOf(defaultValue)).trim().toLowerCase(); + + if ("true".equals(value)) { + return true; + } else if ("false".equals(value)) { + return false; + } + + LOG.warn("Expected boolean for key [" + key + "] instead got [" + value + "]."); + return defaultValue; + } + + public int getInt(String key, int defaultVal) { + String val = get(key); + if (val == null || val.trim().isEmpty()) { + return defaultVal; + } + try { + return Integer.parseInt(val); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException("Bad value for '" + key + "': should be int"); + } + } + + private static class MapConf extends HTraceConfiguration { + private final Map conf; + + public MapConf(Map conf) { + this.conf = new HashMap(conf); + } + + @Override + public String get(String key) { + return conf.get(key); + } + + @Override + public String get(String key, String defaultValue) { + String value = get(key); + return value == null ? defaultValue : value; + } + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java b/htrace-core4/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java new file mode 100644 index 0000000..69a43b1 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java @@ -0,0 +1,257 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.FileSystems; +import java.nio.file.StandardOpenOption; +import java.util.UUID; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Writes the spans it receives to a local file. + */ +public class LocalFileSpanReceiver extends SpanReceiver { + private static final Log LOG = LogFactory.getLog(LocalFileSpanReceiver.class); + public static final String PATH_KEY = "local.file.span.receiver.path"; + public static final String CAPACITY_KEY = "local.file.span.receiver.capacity"; + public static final int CAPACITY_DEFAULT = 5000; + private static ObjectWriter JSON_WRITER = new ObjectMapper().writer(); + private final String path; + + private byte[][] bufferedSpans; + private int bufferedSpansIndex; + private final ReentrantLock bufferLock = new ReentrantLock(); + + private final FileOutputStream stream; + private final FileChannel channel; + private final ReentrantLock channelLock = new ReentrantLock(); + + public LocalFileSpanReceiver(HTraceConfiguration conf) { + int capacity = conf.getInt(CAPACITY_KEY, CAPACITY_DEFAULT); + if (capacity < 1) { + throw new IllegalArgumentException(CAPACITY_KEY + " must not be " + + "less than 1."); + } + String pathStr = conf.get(PATH_KEY); + if (pathStr == null || pathStr.isEmpty()) { + path = getUniqueLocalTraceFileName(); + } else { + path = pathStr; + } + boolean success = false; + try { + this.stream = new FileOutputStream(path, true); + } catch (IOException ioe) { + LOG.error("Error opening " + path + ": " + ioe.getMessage()); + throw new RuntimeException(ioe); + } + this.channel = stream.getChannel(); + if (this.channel == null) { + try { + this.stream.close(); + } catch (IOException e) { + LOG.error("Error closing " + path, e); + } + LOG.error("Failed to get channel for " + path); + throw new RuntimeException("Failed to get channel for " + path); + } + this.bufferedSpans = new byte[capacity][]; + this.bufferedSpansIndex = 0; + if (LOG.isDebugEnabled()) { + LOG.debug("Created new LocalFileSpanReceiver with path = " + path + + ", capacity = " + capacity); + } + } + + /** + * Number of buffers to use in FileChannel#write. + * + * On UNIX, FileChannel#write uses writev-- a kernel interface that allows + * us to send multiple buffers at once. This is more efficient than making a + * separate write call for each buffer, since it minimizes the number of + * transitions from userspace to kernel space. + */ + private final int WRITEV_SIZE = 20; + + private final static ByteBuffer newlineBuf = + ByteBuffer.wrap(new byte[] { (byte)0xa }); + + /** + * Flushes a bufferedSpans array. + */ + private void doFlush(byte[][] toFlush, int len) throws IOException { + int bidx = 0, widx = 0; + ByteBuffer writevBufs[] = new ByteBuffer[2 * WRITEV_SIZE]; + + while (true) { + if (widx == writevBufs.length) { + channel.write(writevBufs); + widx = 0; + } + if (bidx == len) { + break; + } + writevBufs[widx] = ByteBuffer.wrap(toFlush[bidx]); + writevBufs[widx + 1] = newlineBuf; + bidx++; + widx+=2; + } + if (widx > 0) { + channel.write(writevBufs, 0, widx); + } + } + + @Override + public void receiveSpan(Span span) { + // Serialize the span data into a byte[]. Note that we're not holding the + // lock here, to improve concurrency. + byte jsonBuf[] = null; + try { + jsonBuf = JSON_WRITER.writeValueAsBytes(span); + } catch (JsonProcessingException e) { + LOG.error("receiveSpan(path=" + path + ", span=" + span + "): " + + "Json processing error: " + e.getMessage()); + return; + } + + // Grab the bufferLock and put our jsonBuf into the list of buffers to + // flush. + byte toFlush[][] = null; + bufferLock.lock(); + try { + if (bufferedSpans == null) { + LOG.debug("receiveSpan(path=" + path + ", span=" + span + "): " + + "LocalFileSpanReceiver for " + path + " is closed."); + return; + } + bufferedSpans[bufferedSpansIndex] = jsonBuf; + bufferedSpansIndex++; + if (bufferedSpansIndex == bufferedSpans.length) { + // If we've hit the limit for the number of buffers to flush, + // swap out the existing bufferedSpans array for a new array, and + // prepare to flush those spans to disk. + toFlush = bufferedSpans; + bufferedSpansIndex = 0; + bufferedSpans = new byte[bufferedSpans.length][]; + } + } finally { + bufferLock.unlock(); + } + if (toFlush != null) { + // We released the bufferLock above, to avoid blocking concurrent + // receiveSpan calls. But now, we must take the channelLock, to make + // sure that we have sole access to the output channel. If we did not do + // this, we might get interleaved output. + // + // There is a small chance that another thread doing a flush of more + // recent spans could get ahead of us here, and take the lock before we + // do. This is ok, since spans don't have to be written out in order. + channelLock.lock(); + try { + doFlush(toFlush, toFlush.length); + } catch (IOException ioe) { + LOG.error("Error flushing buffers to " + path + ": " + + ioe.getMessage()); + } finally { + channelLock.unlock(); + } + } + } + + @Override + public void close() throws IOException { + byte toFlush[][] = null; + int numToFlush = 0; + bufferLock.lock(); + try { + if (bufferedSpans == null) { + LOG.info("LocalFileSpanReceiver for " + path + " was already closed."); + return; + } + numToFlush = bufferedSpansIndex; + bufferedSpansIndex = 0; + toFlush = bufferedSpans; + bufferedSpans = null; + } finally { + bufferLock.unlock(); + } + channelLock.lock(); + try { + doFlush(toFlush, numToFlush); + } catch (IOException ioe) { + LOG.error("Error flushing buffers to " + path + ": " + + ioe.getMessage()); + } finally { + try { + stream.close(); + } catch (IOException e) { + LOG.error("Error closing stream for " + path, e); + } + channelLock.unlock(); + } + } + + public static String getUniqueLocalTraceFileName() { + String tmp = System.getProperty("java.io.tmpdir", "/tmp"); + String nonce = null; + BufferedReader reader = null; + try { + // On Linux we can get a unique local file name by reading the process id + // out of /proc/self/stat. (There isn't any portable way to get the + // process ID from Java.) + reader = new BufferedReader( + new InputStreamReader(new FileInputStream("/proc/self/stat"), + "UTF-8")); + String line = reader.readLine(); + if (line == null) { + throw new EOFException(); + } + nonce = line.split(" ")[0]; + } catch (IOException e) { + } finally { + if (reader != null) { + try { + reader.close(); + } catch(IOException e) { + LOG.warn("Exception in closing " + reader, e); + } + } + } + if (nonce == null) { + // If we can't use the process ID, use a random nonce. + nonce = UUID.randomUUID().toString(); + } + return new File(tmp, nonce).getAbsolutePath(); + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/MilliSpan.java b/htrace-core4/src/main/java/org/apache/htrace/core/MilliSpan.java new file mode 100644 index 0000000..5dd6bdb --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/MilliSpan.java @@ -0,0 +1,347 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * A Span implementation that stores its information in milliseconds since the + * epoch. + */ +@JsonDeserialize(using = MilliSpan.MilliSpanDeserializer.class) +public class MilliSpan implements Span { + private static ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static ObjectReader JSON_READER = OBJECT_MAPPER.reader(MilliSpan.class); + private static ObjectWriter JSON_WRITER = OBJECT_MAPPER.writer(); + private static final SpanId EMPTY_PARENT_ARRAY[] = new SpanId[0]; + private static final String EMPTY_STRING = ""; + + private long begin; + private long end; + private final String description; + private SpanId parents[]; + private final SpanId spanId; + private Map traceInfo = null; + private String tracerId; + private List timeline = null; + + @Override + public Span child(String childDescription) { + return new MilliSpan.Builder(). + begin(System.currentTimeMillis()). + end(0). + description(childDescription). + parents(new SpanId[] {spanId}). + spanId(spanId.newChildId()). + tracerId(tracerId). + build(); + } + + /** + * The public interface for constructing a MilliSpan. + */ + public static class Builder { + private long begin; + private long end; + private String description = EMPTY_STRING; + private SpanId parents[] = EMPTY_PARENT_ARRAY; + private SpanId spanId = SpanId.INVALID; + private Map traceInfo = null; + private String tracerId = EMPTY_STRING; + private List timeline = null; + + public Builder() { + } + + public Builder begin(long begin) { + this.begin = begin; + return this; + } + + public Builder end(long end) { + this.end = end; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder parents(SpanId parents[]) { + this.parents = parents; + return this; + } + + public Builder parents(List parentList) { + SpanId[] parents = new SpanId[parentList.size()]; + for (int i = 0; i < parentList.size(); i++) { + parents[i] = parentList.get(i); + } + this.parents = parents; + return this; + } + + public Builder spanId(SpanId spanId) { + this.spanId = spanId; + return this; + } + + public Builder traceInfo(Map traceInfo) { + this.traceInfo = traceInfo.isEmpty() ? null : traceInfo; + return this; + } + + public Builder tracerId(String tracerId) { + this.tracerId = tracerId; + return this; + } + + public Builder timeline(List timeline) { + this.timeline = timeline.isEmpty() ? null : timeline; + return this; + } + + public MilliSpan build() { + return new MilliSpan(this); + } + } + + public MilliSpan() { + this.begin = 0; + this.end = 0; + this.description = EMPTY_STRING; + this.parents = EMPTY_PARENT_ARRAY; + this.spanId = SpanId.INVALID; + this.traceInfo = null; + this.tracerId = EMPTY_STRING; + this.timeline = null; + } + + private MilliSpan(Builder builder) { + this.begin = builder.begin; + this.end = builder.end; + this.description = builder.description; + this.parents = builder.parents; + this.spanId = builder.spanId; + this.traceInfo = builder.traceInfo; + this.tracerId = builder.tracerId; + this.timeline = builder.timeline; + } + + @Override + public synchronized void stop() { + if (end == 0) { + if (begin == 0) + throw new IllegalStateException("Span for " + description + + " has not been started"); + end = System.currentTimeMillis(); + } + } + + protected long currentTimeMillis() { + return System.currentTimeMillis(); + } + + @Override + public synchronized boolean isRunning() { + return begin != 0 && end == 0; + } + + @Override + public synchronized long getAccumulatedMillis() { + if (begin == 0) + return 0; + if (end > 0) + return end - begin; + return currentTimeMillis() - begin; + } + + @Override + public String toString() { + return toJson(); + } + + @Override + public String getDescription() { + return description; + } + + @Override + public SpanId getSpanId() { + return spanId; + } + + @Override + public SpanId[] getParents() { + return parents; + } + + @Override + public void setParents(SpanId[] parents) { + this.parents = parents; + } + + @Override + public long getStartTimeMillis() { + return begin; + } + + @Override + public long getStopTimeMillis() { + return end; + } + + @Override + public void addKVAnnotation(String key, String value) { + if (traceInfo == null) + traceInfo = new HashMap(); + traceInfo.put(key, value); + } + + @Override + public void addTimelineAnnotation(String msg) { + if (timeline == null) { + timeline = new ArrayList(); + } + timeline.add(new TimelineAnnotation(System.currentTimeMillis(), msg)); + } + + @Override + public Map getKVAnnotations() { + if (traceInfo == null) + return Collections.emptyMap(); + return Collections.unmodifiableMap(traceInfo); + } + + @Override + public List getTimelineAnnotations() { + if (timeline == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(timeline); + } + + @Override + public String getTracerId() { + return tracerId; + } + + @Override + public void setTracerId(String tracerId) { + this.tracerId = tracerId; + } + + @Override + public String toJson() { + StringWriter writer = new StringWriter(); + try { + JSON_WRITER.writeValue(writer, this); + } catch (IOException e) { + // An IOException should not be possible when writing to a string. + throw new RuntimeException(e); + } + return writer.toString(); + } + + public static class MilliSpanDeserializer + extends JsonDeserializer { + @Override + public MilliSpan deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + JsonNode root = jp.getCodec().readTree(jp); + Builder builder = new Builder(); + JsonNode bNode = root.get("b"); + if (bNode != null) { + builder.begin(bNode.asLong()); + } + JsonNode eNode = root.get("e"); + if (eNode != null) { + builder.end(eNode.asLong()); + } + JsonNode dNode = root.get("d"); + if (dNode != null) { + builder.description(dNode.asText()); + } + JsonNode sNode = root.get("a"); + if (sNode != null) { + builder.spanId(SpanId.fromString(sNode.asText())); + } + JsonNode rNode = root.get("r"); + if (rNode != null) { + builder.tracerId(rNode.asText()); + } + JsonNode parentsNode = root.get("p"); + LinkedList parents = new LinkedList(); + if (parentsNode != null) { + for (Iterator iter = parentsNode.elements(); + iter.hasNext(); ) { + JsonNode parentIdNode = iter.next(); + parents.add(SpanId.fromString(parentIdNode.asText())); + } + } + builder.parents(parents); + JsonNode traceInfoNode = root.get("n"); + if (traceInfoNode != null) { + HashMap traceInfo = new HashMap(); + for (Iterator iter = traceInfoNode.fieldNames(); + iter.hasNext(); ) { + String field = iter.next(); + traceInfo.put(field, traceInfoNode.get(field).asText()); + } + builder.traceInfo(traceInfo); + } + JsonNode timelineNode = root.get("t"); + if (timelineNode != null) { + LinkedList timeline = + new LinkedList(); + for (Iterator iter = timelineNode.elements(); + iter.hasNext(); ) { + JsonNode ann = iter.next(); + timeline.add(new TimelineAnnotation(ann.get("t").asLong(), + ann.get("m").asText())); + } + builder.timeline(timeline); + } + return builder.build(); + } + } + + public static MilliSpan fromJson(String json) throws IOException { + return JSON_READER.readValue(json); + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/NeverSampler.java b/htrace-core4/src/main/java/org/apache/htrace/core/NeverSampler.java new file mode 100644 index 0000000..60cc7d2 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/NeverSampler.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +/** + * A Sampler that never returns true. + */ +public final class NeverSampler extends Sampler { + public static final NeverSampler INSTANCE = new NeverSampler(null); + + public NeverSampler(HTraceConfiguration conf) { + } + + @Override + public boolean next() { + return false; + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/NullScope.java b/htrace-core4/src/main/java/org/apache/htrace/core/NullScope.java new file mode 100644 index 0000000..fe76e46 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/NullScope.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +/** + * An empty {@link TraceScope}. + */ +class NullScope extends TraceScope { + NullScope(Tracer tracer) { + super(tracer, null, null); + } + + @Override + public SpanId getSpanId() { + return SpanId.INVALID; + } + + @Override + public void detach() { + if (detached) { + Tracer.throwClientError("Can't detach this TraceScope because " + + "it is already detached."); + } + detached = true; + } + + @Override + public void reattach() { + if (!detached) { + Tracer.throwClientError("Can't reattach this TraceScope because " + + "it is not detached."); + } + detached = false; + } + + @Override + public void close() { + tracer.popNullScope(); + } + + @Override + public String toString() { + return "NullScope"; + } + + @Override + public void addKVAnnotation(String key, String value) { + // do nothing + } + + @Override + public void addTimelineAnnotation(String msg) { + // do nothing + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java b/htrace-core4/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java new file mode 100644 index 0000000..34322fa --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; + +/** + * SpanReceiver for testing only that just collects the Span objects it + * receives. The spans it receives can be accessed with getSpans(); + */ +public class POJOSpanReceiver extends SpanReceiver { + private final Collection spans; + + public POJOSpanReceiver(HTraceConfiguration conf) { + this.spans = new HashSet(); + } + + /** + * @return The spans this POJOSpanReceiver has received. + */ + public Collection getSpans() { + return spans; + } + + @Override + public void close() throws IOException { + } + + @Override + public void receiveSpan(Span span) { + spans.add(span); + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/ProbabilitySampler.java b/htrace-core4/src/main/java/org/apache/htrace/core/ProbabilitySampler.java new file mode 100644 index 0000000..c0bb16c --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/ProbabilitySampler.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.util.concurrent.ThreadLocalRandom; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Sampler that returns true a certain percentage of the time. Specify the frequency interval by + * configuring a {@code double} value for {@link #SAMPLER_FRACTION_CONF_KEY}. + */ +public class ProbabilitySampler extends Sampler { + private static final Log LOG = LogFactory.getLog(ProbabilitySampler.class); + public final double threshold; + public final static String SAMPLER_FRACTION_CONF_KEY = "sampler.fraction"; + + public ProbabilitySampler(HTraceConfiguration conf) { + this.threshold = Double.parseDouble(conf.get(SAMPLER_FRACTION_CONF_KEY)); + if (LOG.isTraceEnabled()) { + LOG.trace("Created new ProbabilitySampler with threshold = " + + threshold + "."); + } + } + + @Override + public boolean next() { + return ThreadLocalRandom.current().nextDouble() < threshold; + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/Sampler.java b/htrace-core4/src/main/java/org/apache/htrace/core/Sampler.java new file mode 100644 index 0000000..af0165c --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/Sampler.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.lang.reflect.Constructor; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Extremely simple callback to determine the frequency that an action should be + * performed. + *

+ * For example, the next() function may look like this: + *

+ *

+ * 
+ * public boolean next() {
+ *   return Math.random() > 0.5;
+ * }
+ * 
+ * 
+ * This would trace 50% of all gets, 75% of all puts and would not trace any other requests. + */ +public abstract class Sampler { + /** + * A {@link Sampler} builder. It takes a {@link Sampler} class name and + * constructs an instance of that class, with the provided configuration. + */ + public static class Builder { + private static final Log LOG = LogFactory.getLog(Builder.class); + + private final static String DEFAULT_PACKAGE = "org.apache.htrace.core"; + private final HTraceConfiguration conf; + private String className; + private ClassLoader classLoader = Builder.class.getClassLoader(); + + public Builder(HTraceConfiguration conf) { + this.conf = conf; + reset(); + } + + public Builder reset() { + this.className = null; + return this; + } + + public Builder className(String className) { + this.className = className; + return this; + } + + public Builder classLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + return this; + } + + private void throwError(String errorStr) { + LOG.error(errorStr); + throw new RuntimeException(errorStr); + } + + private void throwError(String errorStr, Throwable e) { + LOG.error(errorStr, e); + throw new RuntimeException(errorStr, e); + } + + public Sampler build() { + Sampler sampler = newSampler(); + if (LOG.isTraceEnabled()) { + LOG.trace("Created new sampler of type " + + sampler.getClass().getName(), new Exception()); + } + return sampler; + } + + private Sampler newSampler() { + if (className == null || className.isEmpty()) { + throwError("No sampler class specified."); + } + String str = className; + if (!str.contains(".")) { + str = DEFAULT_PACKAGE + "." + str; + } + Class cls = null; + try { + cls = classLoader.loadClass(str); + } catch (ClassNotFoundException e) { + throwError("Cannot find Sampler class " + str); + } + Constructor ctor = null; + try { + ctor = cls.getConstructor(HTraceConfiguration.class); + } catch (NoSuchMethodException e) { + throwError("Cannot find a constructor for class " + + str + "which takes an HTraceConfiguration."); + } + Sampler sampler = null; + try { + LOG.debug("Creating new instance of " + str + "..."); + sampler = ctor.newInstance(conf); + } catch (ReflectiveOperationException e) { + throwError("Reflection error when constructing " + + str + ".", e); + } catch (Throwable t) { + throwError("NewInstance error when constructing " + + str + ".", t); + } + return sampler; + } + } + + public static final Sampler ALWAYS = AlwaysSampler.INSTANCE; + public static final Sampler NEVER = NeverSampler.INSTANCE; + + public abstract boolean next(); +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/Span.java b/htrace-core4/src/main/java/org/apache/htrace/core/Span.java new file mode 100644 index 0000000..e63d414 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/Span.java @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +/** + * Base interface for gathering and reporting statistics about a block of + * execution. + *

+ * Spans should form a directed acyclic graph structure. It should be possible + * to keep following the parents of a span until you arrive at a span with no + * parents.

+ */ +@JsonSerialize(using = Span.SpanSerializer.class) +public interface Span { + /** + * The block has completed, stop the clock + */ + void stop(); + + /** + * Get the start time, in milliseconds + */ + long getStartTimeMillis(); + + /** + * Get the stop time, in milliseconds + */ + long getStopTimeMillis(); + + /** + * Return the total amount of time elapsed since start was called, if running, + * or difference between stop and start + */ + long getAccumulatedMillis(); + + /** + * Has the span been started and not yet stopped? + */ + boolean isRunning(); + + /** + * Return a textual description of this span.

+ * + * Will never be null. + */ + String getDescription(); + + /** + * A pseudo-unique (random) number assigned to this span instance.

+ * + * The spanId is immutable and cannot be changed. It is safe to access this + * from multiple threads. + */ + SpanId getSpanId(); + + /** + * Create a child span of this span with the given description + * @deprecated Since 4.0.0. Use {@link MilliSpan.Builder} + */ + @Deprecated + Span child(String description); + + @Override + String toString(); + + /** + * Returns the parent IDs of the span.

+ * + * The array will be empty if there are no parents. + */ + SpanId[] getParents(); + + /** + * Set the parents of this span.

+ * + * Any existing parents will be cleared by this call. + */ + void setParents(SpanId[] parents); + + /** + * Add a data annotation associated with this span + */ + void addKVAnnotation(String key, String value); + + /** + * Add a timeline annotation associated with this span + */ + void addTimelineAnnotation(String msg); + + /** + * Get data associated with this span (read only)

+ * + * Will never be null. + */ + Map getKVAnnotations(); + + /** + * Get any timeline annotations (read only)

+ * + * Will never be null. + */ + List getTimelineAnnotations(); + + /** + * Return a unique id for the process from which this Span originated.

+ * + * Will never be null. + */ + String getTracerId(); + + /** + * Set the process id of a span. + */ + void setTracerId(String s); + + /** + * Serialize to Json + */ + String toJson(); + + public static class SpanSerializer extends JsonSerializer { + @Override + public void serialize(Span span, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + jgen.writeStartObject(); + if (span.getSpanId().isValid()) { + jgen.writeStringField("a", span.getSpanId().toString()); + } + if (span.getStartTimeMillis() != 0) { + jgen.writeNumberField("b", span.getStartTimeMillis()); + } + if (span.getStopTimeMillis() != 0) { + jgen.writeNumberField("e", span.getStopTimeMillis()); + } + if (!span.getDescription().isEmpty()) { + jgen.writeStringField("d", span.getDescription()); + } + String tracerId = span.getTracerId(); + if (!tracerId.isEmpty()) { + jgen.writeStringField("r", tracerId); + } + jgen.writeArrayFieldStart("p"); + for (SpanId parent : span.getParents()) { + jgen.writeString(parent.toString()); + } + jgen.writeEndArray(); + Map traceInfoMap = span.getKVAnnotations(); + if (!traceInfoMap.isEmpty()) { + jgen.writeObjectFieldStart("n"); + for (Map.Entry e : traceInfoMap.entrySet()) { + jgen.writeStringField(e.getKey(), e.getValue()); + } + jgen.writeEndObject(); + } + List timelineAnnotations = + span.getTimelineAnnotations(); + if (!timelineAnnotations.isEmpty()) { + jgen.writeArrayFieldStart("t"); + for (TimelineAnnotation tl : timelineAnnotations) { + jgen.writeStartObject(); + jgen.writeNumberField("t", tl.getTime()); + jgen.writeStringField("m", tl.getMessage()); + jgen.writeEndObject(); + } + jgen.writeEndArray(); + } + jgen.writeEndObject(); + } + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/SpanId.java b/htrace-core4/src/main/java/org/apache/htrace/core/SpanId.java new file mode 100644 index 0000000..ed31ad3 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/SpanId.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.util.concurrent.ThreadLocalRandom; + +/** + * Uniquely identifies an HTrace span. + * + * Span IDs are 128 bits in total. The upper 64 bits of a span ID is the same + * as the upper 64 bits of the parent span, if there is one. The lower 64 bits + * are always random. + */ +public final class SpanId implements Comparable { + private static final int SPAN_ID_STRING_LENGTH = 32; + private final long high; + private final long low; + + /** + * The invalid span ID, which is all zeroes. + * + * It is also the "least" span ID in the sense that it is considered + * smaller than any other span ID. + */ + public static SpanId INVALID = new SpanId(0, 0); + + private static long nonZeroRand64() { + while (true) { + long r = ThreadLocalRandom.current().nextLong(); + if (r != 0) { + return r; + } + } + } + + public static SpanId fromRandom() { + return new SpanId(nonZeroRand64(), nonZeroRand64()); + } + + public static SpanId fromString(String str) { + if (str.length() != SPAN_ID_STRING_LENGTH) { + throw new RuntimeException("Invalid SpanID string: length was not " + + SPAN_ID_STRING_LENGTH); + } + long high = + ((Long.parseLong(str.substring(0, 8), 16)) << 32) | + (Long.parseLong(str.substring(8, 16), 16)); + long low = + ((Long.parseLong(str.substring(16, 24), 16)) << 32) | + (Long.parseLong(str.substring(24, 32), 16)); + return new SpanId(high, low); + } + + public SpanId(long high, long low) { + this.high = high; + this.low = low; + } + + public long getHigh() { + return high; + } + + public long getLow() { + return low; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof SpanId)) { + return false; + } + SpanId other = (SpanId)o; + return ((other.high == high) && (other.low == low)); + } + + @Override + public int compareTo(SpanId other) { + int cmp = compareAsUnsigned(high, other.high); + if (cmp != 0) { + return cmp; + } + return compareAsUnsigned(low, other.low); + } + + private static int compareAsUnsigned(long a, long b) { + boolean aSign = a < 0; + boolean bSign = b < 0; + if (aSign != bSign) { + if (aSign) { + return 1; + } else { + return -1; + } + } + if (aSign) { + a = -a; + b = -b; + } + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else { + return 0; + } + } + + @Override + public int hashCode() { + return (int)((0xffffffff & (high >> 32))) ^ + (int)((0xffffffff & (high >> 0))) ^ + (int)((0xffffffff & (low >> 32))) ^ + (int)((0xffffffff & (low >> 0))); + } + + @Override + public String toString() { + return String.format("%08x%08x%08x%08x", + (0x00000000ffffffffL & (high >> 32)), + (0x00000000ffffffffL & high), + (0x00000000ffffffffL & (low >> 32)), + (0x00000000ffffffffL & low)); + } + + public boolean isValid() { + return (high != 0) || (low != 0); + } + + public SpanId newChildId() { + return new SpanId(high, nonZeroRand64()); + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/SpanReceiver.java b/htrace-core4/src/main/java/org/apache/htrace/core/SpanReceiver.java new file mode 100644 index 0000000..a955ddf --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/SpanReceiver.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.io.Closeable; +import java.lang.reflect.Constructor; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * The collector within a process that is the destination of Spans when a trace is running. + * {@code SpanReceiver} implementations are expected to provide a constructor with the signature + *

+ *

+ * public SpanReceiverImpl(HTraceConfiguration)
+ * 
+ */ +public abstract class SpanReceiver implements Closeable { + /** + * A {@link SpanReceiver} builder. It takes a {@link SpanReceiver} class name + * and constructs an instance of that class, with the provided configuration. + */ + public static class Builder { + private static final Log LOG = LogFactory.getLog(Builder.class); + + private final static String DEFAULT_PACKAGE = "org.apache.htrace.core"; + private final HTraceConfiguration conf; + private boolean logErrors; + private String className; + private ClassLoader classLoader = Builder.class.getClassLoader(); + + public Builder(HTraceConfiguration conf) { + this.conf = conf; + reset(); + } + + /** + * Set this builder back to defaults. + * + * @return this instance. + */ + public Builder reset() { + this.logErrors = true; + this.className = null; + return this; + } + + public Builder className(final String className) { + this.className = className; + return this; + } + + /** + * Configure whether we should log errors during build(). + * @return This instance + */ + public Builder logErrors(boolean logErrors) { + this.logErrors = logErrors; + return this; + } + + public Builder classLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + return this; + } + + private void throwError(String errorStr) { + if (logErrors) { + LOG.error(errorStr); + } + throw new RuntimeException(errorStr); + } + + private void throwError(String errorStr, Throwable e) { + if (logErrors) { + LOG.error(errorStr, e); + } + throw new RuntimeException(errorStr, e); + } + + public SpanReceiver build() { + SpanReceiver spanReceiver = newSpanReceiver(); + if (LOG.isTraceEnabled()) { + LOG.trace("Created new span receiver of type " + + spanReceiver.getClass().getName()); + } + return spanReceiver; + } + + private SpanReceiver newSpanReceiver() { + if ((className == null) || className.isEmpty()) { + throwError("No span receiver class specified."); + } + String str = className; + if (!str.contains(".")) { + str = DEFAULT_PACKAGE + "." + str; + } + Class cls = null; + try { + cls = classLoader.loadClass(str); + } catch (ClassNotFoundException e) { + throwError("Cannot find SpanReceiver class " + str); + } + Constructor ctor = null; + try { + ctor = cls.getConstructor(HTraceConfiguration.class); + } catch (NoSuchMethodException e) { + throwError("Cannot find a constructor for class " + + str + "which takes an HTraceConfiguration."); + } + SpanReceiver receiver = null; + try { + LOG.debug("Creating new instance of " + str + "..."); + receiver = ctor.newInstance(conf); + } catch (ReflectiveOperationException e) { + throwError("Reflection error when constructing " + + str + ".", e); + } catch (Throwable t) { + throwError("NewInstance error when constructing " + + str + ".", t); + } + return receiver; + } + } + + /** + * An ID which uniquely identifies this SpanReceiver. + */ + private final long id; + + private static final AtomicLong HIGHEST_SPAN_RECEIVER_ID = new AtomicLong(0); + + /** + * Get an ID uniquely identifying this SpanReceiver. + */ + public final long getId() { + return id; + } + + protected SpanReceiver() { + this.id = HIGHEST_SPAN_RECEIVER_ID.incrementAndGet(); + } + + /** + * Called when a Span is stopped and can now be stored. + */ + public abstract void receiveSpan(Span span); +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java b/htrace-core4/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java new file mode 100644 index 0000000..f443ec6 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +/** + * Used for testing. Simply prints to standard out any spans it receives. + */ +public class StandardOutSpanReceiver extends SpanReceiver { + private static final Log LOG = LogFactory.getLog(StandardOutSpanReceiver.class); + + public StandardOutSpanReceiver(HTraceConfiguration conf) { + LOG.trace("Created new StandardOutSpanReceiver."); + } + + @Override + public void receiveSpan(Span span) { + System.out.println(span); + } + + @Override + public void close() throws IOException { + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TimelineAnnotation.java b/htrace-core4/src/main/java/org/apache/htrace/core/TimelineAnnotation.java new file mode 100644 index 0000000..18de061 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/TimelineAnnotation.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +public class TimelineAnnotation { + private final long time; + private final String msg; + + public TimelineAnnotation(long time, String msg) { + this.time = time; + this.msg = msg; + } + + public long getTime() { + return time; + } + + public String getMessage() { + return msg; + } + + @Override + public String toString() { + return "@" + time + ": " + msg; + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TraceCallable.java b/htrace-core4/src/main/java/org/apache/htrace/core/TraceCallable.java new file mode 100644 index 0000000..a0fec17 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/TraceCallable.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.util.concurrent.Callable; + +/** + * Wrap a Callable with a Span that survives a change in threads. + */ +public class TraceCallable implements Callable { + private final Tracer tracer; + private final Callable impl; + private final TraceScope parent; + private final String description; + + TraceCallable(Tracer tracer, TraceScope parent, Callable impl, + String description) { + this.tracer = tracer; + this.impl = impl; + this.parent = parent; + if (description == null) { + this.description = Thread.currentThread().getName(); + } else { + this.description = description; + } + } + + @Override + public V call() throws Exception { + TraceScope chunk = tracer.newScope(description, + parent.getSpan().getSpanId()); + try { + return impl.call(); + } finally { + chunk.close(); + } + } + + public Callable getImpl() { + return impl; + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TraceExecutorService.java b/htrace-core4/src/main/java/org/apache/htrace/core/TraceExecutorService.java new file mode 100644 index 0000000..81e31ea --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/TraceExecutorService.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class TraceExecutorService implements ExecutorService { + private final Tracer tracer; + private final String scopeName; + private final ExecutorService impl; + + TraceExecutorService(Tracer tracer, String scopeName, + ExecutorService impl) { + this.tracer = tracer; + this.scopeName = scopeName; + this.impl = impl; + } + + @Override + public void execute(Runnable command) { + impl.execute(tracer.wrap(command, scopeName)); + } + + @Override + public void shutdown() { + impl.shutdown(); + } + + @Override + public List shutdownNow() { + return impl.shutdownNow(); + } + + @Override + public boolean isShutdown() { + return impl.isShutdown(); + } + + @Override + public boolean isTerminated() { + return impl.isTerminated(); + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) + throws InterruptedException { + return impl.awaitTermination(timeout, unit); + } + + @Override + public Future submit(Callable task) { + return impl.submit(tracer.wrap(task, scopeName)); + } + + @Override + public Future submit(Runnable task, T result) { + return impl.submit(tracer.wrap(task, scopeName), result); + } + + @Override + public Future submit(Runnable task) { + return impl.submit(tracer.wrap(task, scopeName)); + } + + private Collection> wrapCollection( + Collection> tasks) { + List> result = new ArrayList>(); + for (Callable task : tasks) { + result.add(tracer.wrap(task, scopeName)); + } + return result; + } + + @Override + public List> invokeAll(Collection> tasks) + throws InterruptedException { + return impl.invokeAll(wrapCollection(tasks)); + } + + @Override + public List> invokeAll(Collection> tasks, + long timeout, TimeUnit unit) throws InterruptedException { + return impl.invokeAll(wrapCollection(tasks), timeout, unit); + } + + @Override + public T invokeAny(Collection> tasks) + throws InterruptedException, ExecutionException { + return impl.invokeAny(wrapCollection(tasks)); + } + + @Override + public T invokeAny(Collection> tasks, long timeout, + TimeUnit unit) throws InterruptedException, ExecutionException, + TimeoutException { + return impl.invokeAny(wrapCollection(tasks), timeout, unit); + } + +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TraceRunnable.java b/htrace-core4/src/main/java/org/apache/htrace/core/TraceRunnable.java new file mode 100644 index 0000000..8f98708 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/TraceRunnable.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +/** + * Wrap a Runnable with a Span that survives a change in threads. + */ +public class TraceRunnable implements Runnable { + private final Tracer tracer; + private final TraceScope parent; + private final Runnable runnable; + private final String description; + + public TraceRunnable(Tracer tracer, TraceScope parent, + Runnable runnable, String description) { + this.tracer = tracer; + this.parent = parent; + this.runnable = runnable; + if (description == null) { + this.description = Thread.currentThread().getName(); + } else { + this.description = description; + } + } + + @Override + public void run() { + TraceScope chunk = tracer.newScope(description, + parent.getSpan().getSpanId()); + try { + runnable.run(); + } finally { + chunk.close(); + } + } + + public Runnable getRunnable() { + return runnable; + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TraceScope.java b/htrace-core4/src/main/java/org/apache/htrace/core/TraceScope.java new file mode 100644 index 0000000..05a053e --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/TraceScope.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.io.Closeable; + +/** + * Create a new TraceScope at major transitions. Hosts current tracing context. + */ +public class TraceScope implements Closeable { + /** + * The tracer to use for this scope. + */ + final Tracer tracer; + + /** + * The trace span for this scope, or null if the scope is closed. + * + * If the scope is closed, it must also be detached. + */ + private final Span span; + + /** + * The parent of this trace scope, or null if there is no parent. + */ + private TraceScope parent; + + /** + * True if this scope is detached. + */ + boolean detached; + + TraceScope(Tracer tracer, Span span, TraceScope parent) { + this.tracer = tracer; + this.span = span; + this.parent = parent; + this.detached = false; + } + + /** + * Returns the span which this scope is managing. + */ + public Span getSpan() { + return span; + } + + /** + * Returns the span ID which this scope is managing. + */ + public SpanId getSpanId() { + return span.getSpanId(); + } + + TraceScope getParent() { + return parent; + } + + void setParent(TraceScope parent) { + this.parent = parent; + } + + /** + * Detach this TraceScope from the current thread. + * + * It is OK to "leak" TraceScopes which have been detached. They will not + * consume any resources other than a small amount of memory until they are + * garbage collected. On the other hand, trace scopes which are still + * attached must never be leaked. + */ + public void detach() { + if (detached) { + Tracer.throwClientError("Can't detach this TraceScope because " + + "it is already detached."); + } + tracer.detachScope(this); + detached = true; + parent = null; + } + + /** + * Attach this TraceScope to the current thread. + */ + public void reattach() { + if (!detached) { + Tracer.throwClientError("Can't reattach this TraceScope because " + + "it is not detached."); + } + tracer.reattachScope(this); + detached = false; + } + + /** + * Close this TraceScope, ending the trace span it is managing. + */ + @Override + public void close() { + tracer.closeScope(this); + } + + public void addKVAnnotation(String key, String value) { + span.addKVAnnotation(key, value); + } + + public void addTimelineAnnotation(String msg) { + span.addTimelineAnnotation(msg); + } + + @Override + public String toString() { + return "TraceScope(tracerId=" + tracer.getTracerId() + + ", span=" + span.toJson() + + ", detached=" + detached + ")"; + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/Tracer.java b/htrace-core4/src/main/java/org/apache/htrace/core/Tracer.java new file mode 100644 index 0000000..39d972d --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/Tracer.java @@ -0,0 +1,673 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.io.Closeable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Use a Tracer instance inside a 'process' to collect and distribute its trace Spans. + * Example processes are an HDFS DataNode or an HBase RegionServer. A Tracer instance is your + * one-stop shop for all things tracing. + * + *

+ */ +public class Tracer implements Closeable { + private static final Log LOG = LogFactory.getLog(Tracer.class); + + public final static String SPAN_RECEIVER_CLASSES_KEY = "span.receiver.classes"; + public final static String SAMPLER_CLASSES_KEY = "sampler.classes"; + + public static class Builder { + private String name; + private HTraceConfiguration conf = HTraceConfiguration.EMPTY; + private ClassLoader classLoader = + Builder.class.getClassLoader(); + private TracerPool tracerPool = TracerPool.GLOBAL; + + /** + * @deprecated Since 4.0.0. Use Constructor that takes a name argument instead + */ + @Deprecated + public Builder() { + } + + public Builder(final String name) { + name(name); + } + + /** + * @param name + * @return This + * @deprecated Since 4.0.0. Use Constructor that takes a name argument instead. + */ + @Deprecated + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder conf(HTraceConfiguration conf) { + this.conf = conf; + return this; + } + + public Builder tracerPool(TracerPool tracerPool) { + this.tracerPool = tracerPool; + return this; + } + + private void loadSamplers(List samplers) { + String classNamesStr = conf.get(SAMPLER_CLASSES_KEY, ""); + List classNames = getClassNamesFromConf(classNamesStr); + StringBuilder bld = new StringBuilder(); + String prefix = ""; + for (String className : classNames) { + try { + Sampler sampler = new Sampler.Builder(conf). + className(className). + classLoader(classLoader). + build(); + samplers.add(sampler); + bld.append(prefix).append(className); + prefix = ", "; + } catch (Throwable e) { + LOG.error("Failed to create SpanReceiver of type " + className, e); + } + } + String resultString = bld.toString(); + if (resultString.isEmpty()) { + resultString = "no samplers"; + } + LOG.debug(SAMPLER_CLASSES_KEY + " = " + classNamesStr + + "; loaded " + resultString); + } + + private void loadSpanReceivers() { + String classNamesStr = conf.get(SPAN_RECEIVER_CLASSES_KEY, ""); + List classNames = getClassNamesFromConf(classNamesStr); + StringBuilder bld = new StringBuilder(); + String prefix = ""; + for (String className : classNames) { + try { + tracerPool.loadReceiverType(className, conf, classLoader); + bld.append(prefix).append(className); + prefix = ", "; + } catch (Throwable e) { + LOG.error("Failed to create SpanReceiver of type " + className, e); + } + } + String resultString = bld.toString(); + if (resultString.isEmpty()) { + resultString = "no span receivers"; + } + LOG.debug(SPAN_RECEIVER_CLASSES_KEY + " = " + classNamesStr + + "; loaded " + resultString); + } + + /** + * Get a list of class names from the HTrace configuration. + * Entries which are empty will be removed. Entries which lack a package will + * be given the default package. + * + * @param classNamesStr A semicolon-separated string containing a list + * of class names. + * @return A list of class names. + */ + private List getClassNamesFromConf(String classNamesStr) { + String classNames[] = classNamesStr.split(";"); + LinkedList cleanedClassNames = new LinkedList(); + for (String className : classNames) { + String cleanedClassName = className.trim(); + if (!cleanedClassName.isEmpty()) { + cleanedClassNames.add(cleanedClassName); + } + } + return cleanedClassNames; + } + + public Tracer build() { + if (name == null) { + throw new RuntimeException("You must specify a name for this Tracer."); + } + LinkedList samplers = new LinkedList(); + loadSamplers(samplers); + String tracerId = new TracerId(conf, name).get(); + Tracer tracer = new Tracer(tracerId, tracerPool, + samplers.toArray(new Sampler[samplers.size()])); + tracerPool.addTracer(tracer); + loadSpanReceivers(); + if (LOG.isTraceEnabled()) { + LOG.trace("Created " + tracer + " for " + name); + } + return tracer; + } + } + + /** + * The thread-specific context for this Tracer. + * + * This tracks the current number of trace scopes in a particular thread + * created by this tracer. We use this to apply our samplers only for the + * "top-level" spans. + * + * Note that we can't put the TraceScope objects themselves in this context, + * since we need to be able to use TraceScopes created by other Tracers, and + * this context is per-Tracer. + */ + private static class ThreadContext { + private long depth; + + ThreadContext() { + this.depth = 0; + } + + boolean isTopLevel() { + return (depth == 0); + } + + void pushScope() { + depth++; + } + + TraceScope pushNewScope(Tracer tracer, Span span, TraceScope parentScope) { + TraceScope scope = new TraceScope(tracer, span, parentScope); + threadLocalScope.set(scope); + depth++; + return scope; + } + + void popScope() { + if (depth <= 0) { + throwClientError("There were more trace scopes closed than " + + "were opened."); + } + depth--; + } + }; + + /** + * A subclass of ThreadLocal that starts off with a non-null initial value in + * each thread. + */ + private static class ThreadLocalContext extends ThreadLocal { + @Override + protected ThreadContext initialValue() { + return new ThreadContext(); + } + }; + + /** + * The current trace scope. This is global, so it is shared amongst all + * libraries using HTrace. + */ + final static ThreadLocal threadLocalScope = + new ThreadLocal(); + + /** + * An empty array of SpanId objects. Can be used rather than constructing a + * new object whenever we need an empty array. + */ + private static final SpanId EMPTY_PARENT_ARRAY[] = new SpanId[0]; + + /** + * The tracerId. + */ + private final String tracerId; + + /** + * The TracerPool which this Tracer belongs to. + * + * This gets set to null after the Tracer is closed in order to catch some + * use-after-close errors. Note that we do not synchronize access on this + * field, since it only changes when the Tracer is closed, and the Tracer + * should not be used after that. + */ + private TracerPool tracerPool; + + /** + * The current thread-local context for this particualr Tracer. + */ + private final ThreadLocalContext threadContext; + + /** + * The NullScope instance for this Tracer. + */ + private final NullScope nullScope; + + /** + * The currently active Samplers. + * + * Arrays are immutable once set. You must take the Tracer lock in order to + * set this to a new array. If this is null, the Tracer is closed. + */ + private volatile Sampler[] curSamplers; + + /** + * Log a client error, and throw an exception. + * + * @param str The message to use in the log and the exception. + */ + static void throwClientError(String str) { + LOG.error(str); + throw new RuntimeException(str); + } + + /** + * @return If the current thread is tracing, this function returns the Tracer that is + * being used; otherwise, it returns null. + */ + public static Tracer curThreadTracer() { + TraceScope traceScope = threadLocalScope.get(); + if (traceScope == null) { + return null; + } + return traceScope.tracer; + } + + Tracer(String tracerId, TracerPool tracerPool, Sampler[] curSamplers) { + this.tracerId = tracerId; + this.tracerPool = tracerPool; + this.threadContext = new ThreadLocalContext(); + this.nullScope = new NullScope(this); + this.curSamplers = curSamplers; + } + + public String getTracerId() { + return tracerId; + } + + private TraceScope newScopeImpl(ThreadContext context, String description) { + Span span = new MilliSpan.Builder(). + tracerId(tracerId). + begin(System.currentTimeMillis()). + description(description). + parents(EMPTY_PARENT_ARRAY). + spanId(SpanId.fromRandom()). + build(); + return context.pushNewScope(this, span, null); + } + + private TraceScope newScopeImpl(ThreadContext context, String description, + TraceScope parentScope) { + SpanId parentId = parentScope.getSpan().getSpanId(); + Span span = new MilliSpan.Builder(). + tracerId(tracerId). + begin(System.currentTimeMillis()). + description(description). + parents(new SpanId[] { parentId }). + spanId(parentId.newChildId()). + build(); + return context.pushNewScope(this, span, parentScope); + } + + private TraceScope newScopeImpl(ThreadContext context, String description, + SpanId parentId) { + Span span = new MilliSpan.Builder(). + tracerId(tracerId). + begin(System.currentTimeMillis()). + description(description). + parents(new SpanId[] { parentId }). + spanId(parentId.newChildId()). + build(); + return context.pushNewScope(this, span, null); + } + + private TraceScope newScopeImpl(ThreadContext context, String description, + TraceScope parentScope, SpanId secondParentId) { + SpanId parentId = parentScope.getSpan().getSpanId(); + Span span = new MilliSpan.Builder(). + tracerId(tracerId). + begin(System.currentTimeMillis()). + description(description). + parents(new SpanId[] { parentId, secondParentId }). + spanId(parentId.newChildId()). + build(); + return context.pushNewScope(this, span, parentScope); + } + + /** + * Create a new trace scope. + * + * If there are no scopes above the current scope, we will apply our + * configured samplers. Otherwise, we will create a trace Span only if this thread + * is already tracing, or if the passed parentID was valid. + * + * @param description The description of the new span to create. + * @param parentId If this is a valid span ID, it will be added to + * the parents of the new span we create. + * @return The new trace scope. + */ + public TraceScope newScope(String description, SpanId parentId) { + TraceScope parentScope = threadLocalScope.get(); + ThreadContext context = threadContext.get(); + if (parentScope != null) { + if (parentId.isValid() && + (!parentId.equals(parentScope.getSpan().getSpanId()))) { + return newScopeImpl(context, description, parentScope, parentId); + } else { + return newScopeImpl(context, description, parentScope); + } + } else if (parentId.isValid()) { + return newScopeImpl(context, description, parentId); + } + if (!context.isTopLevel()) { + context.pushScope(); + return nullScope; + } + if (!sample()) { + context.pushScope(); + return nullScope; + } + return newScopeImpl(context, description); + } + + /** + * Create a new trace scope. + * + * If there are no scopes above the current scope, we will apply our + * configured samplers. Otherwise, we will create a trace Span only if this thread + * is already tracing. + * @param description The description of the new span to create. + * @return The new trace scope. + */ + public TraceScope newScope(String description) { + TraceScope parentScope = threadLocalScope.get(); + ThreadContext context = threadContext.get(); + if (parentScope != null) { + return newScopeImpl(context, description, parentScope); + } + if (!context.isTopLevel()) { + context.pushScope(); + return nullScope; + } + if (!sample()) { + context.pushScope(); + return nullScope; + } + return newScopeImpl(context, description); + } + + /** + * Return a null trace scope. + */ + public TraceScope newNullScope() { + ThreadContext context = threadContext.get(); + context.pushScope(); + return nullScope; + } + + /** + * Wrap the callable in a TraceCallable, if tracing. + * + * @return The callable provided, wrapped if tracing, 'callable' if not. + */ + public Callable wrap(Callable callable, String description) { + TraceScope parentScope = threadLocalScope.get(); + if (parentScope == null) { + return callable; + } + return new TraceCallable(this, parentScope, callable, description); + } + + /** + * Wrap the runnable in a TraceRunnable, if tracing + * + * @return The runnable provided, wrapped if tracing, 'runnable' if not. + */ + public Runnable wrap(Runnable runnable, String description) { + TraceScope parentScope = threadLocalScope.get(); + if (parentScope == null) { + return runnable; + } + return new TraceRunnable(this, parentScope, runnable, description); + } + + public TraceExecutorService newTraceExecutorService(ExecutorService impl, + String scopeName) { + return new TraceExecutorService(this, scopeName, impl); + } + + public TracerPool getTracerPool() { + if (tracerPool == null) { + throwClientError(toString() + " is closed."); + } + return tracerPool; + } + + /** + * Returns an object that will trace all calls to itself. + */ + @SuppressWarnings("unchecked") + T createProxy(final T instance) { + final Tracer tracer = this; + InvocationHandler handler = new InvocationHandler() { + @Override + public Object invoke(Object obj, Method method, Object[] args) + throws Throwable { + TraceScope scope = tracer.newScope(method.getName()); + try { + return method.invoke(instance, args); + } catch (Throwable ex) { + ex.printStackTrace(); + throw ex; + } finally { + scope.close(); + } + } + }; + return (T) Proxy.newProxyInstance(instance.getClass().getClassLoader(), + instance.getClass().getInterfaces(), handler); + } + + /** + * Return true if we should create a new top-level span. + * + * We will create the span if any configured sampler returns true. + */ + private boolean sample() { + Sampler[] samplers = curSamplers; + for (Sampler sampler : samplers) { + if (sampler.next()) { + return true; + } + } + return false; + } + + /** + * Returns an array of all the current Samplers. + * + * Note that if the current Samplers change, those changes will not be + * reflected in this array. In other words, this array may be stale. + */ + public Sampler[] getSamplers() { + return curSamplers; + } + + /** + * Add a new Sampler. + * + * @param sampler The new sampler to add. + * You cannot add a particular Sampler object more + * than once. You may add multiple Sampler objects + * of the same type, although this is not recommended. + * + * @return True if the sampler was added; false if it already had + * been added earlier. + */ + public synchronized boolean addSampler(Sampler sampler) { + if (tracerPool == null) { + throwClientError(toString() + " is closed."); + } + Sampler[] samplers = curSamplers; + for (int i = 0; i < samplers.length; i++) { + if (samplers[i] == sampler) { + return false; + } + } + Sampler[] newSamplers = + Arrays.copyOf(samplers, samplers.length + 1); + newSamplers[samplers.length] = sampler; + curSamplers = newSamplers; + return true; + } + + /** + * Remove a SpanReceiver. + * + * @param sampler The sampler to remove. + */ + public synchronized boolean removeSampler(Sampler sampler) { + if (tracerPool == null) { + throwClientError(toString() + " is closed."); + } + Sampler[] samplers = curSamplers; + for (int i = 0; i < samplers.length; i++) { + if (samplers[i] == sampler) { + Sampler[] newSamplers = new Sampler[samplers.length - 1]; + System.arraycopy(samplers, 0, newSamplers, 0, i); + System.arraycopy(samplers, i + 1, newSamplers, i, + samplers.length - i - 1); + curSamplers = newSamplers; + return true; + } + } + return false; + } + + void detachScope(TraceScope scope) { + TraceScope curScope = threadLocalScope.get(); + if (curScope != scope) { + throwClientError("Can't detach TraceScope for " + + scope.getSpan().toJson() + " because it is not the current " + + "TraceScope in thread " + Thread.currentThread().getName()); + } + ThreadContext context = threadContext.get(); + context.popScope(); + threadLocalScope.set(scope.getParent()); + } + + void reattachScope(TraceScope scope) { + TraceScope parent = threadLocalScope.get(); + Tracer.threadLocalScope.set(scope); + ThreadContext context = threadContext.get(); + context.pushScope(); + scope.setParent(parent); + } + + void closeScope(TraceScope scope) { + TraceScope curScope = threadLocalScope.get(); + if (curScope != scope) { + throwClientError("Can't close TraceScope for " + + scope.getSpan().toJson() + " because it is not the current " + + "TraceScope in thread " + Thread.currentThread().getName()); + } + if (tracerPool == null) { + throwClientError(toString() + " is closed."); + } + SpanReceiver[] receivers = tracerPool.getReceivers(); + if (receivers == null) { + throwClientError(toString() + " is closed."); + } + ThreadContext context = threadContext.get(); + context.popScope(); + threadLocalScope.set(scope.getParent()); + scope.setParent(null); + Span span = scope.getSpan(); + span.stop(); + for (SpanReceiver receiver : receivers) { + receiver.receiveSpan(span); + } + } + + void popNullScope() { + TraceScope curScope = threadLocalScope.get(); + if (curScope != null) { + throwClientError("Attempted to close an empty scope, but it was not " + + "the current thread scope in thread " + + Thread.currentThread().getName()); + } + ThreadContext context = threadContext.get(); + context.popScope(); + } + + public static Span getCurrentSpan() { + TraceScope curScope = threadLocalScope.get(); + if (curScope == null) { + return null; + } else { + return curScope.getSpan(); + } + } + + public static SpanId getCurrentSpanId() { + TraceScope curScope = threadLocalScope.get(); + if (curScope == null) { + return SpanId.INVALID; + } else { + return curScope.getSpan().getSpanId(); + } + } + + @Override + public synchronized void close() { + if (tracerPool == null) { + return; + } + curSamplers = new Sampler[0]; + tracerPool.removeTracer(this); + } + + /** + * Get the hash code of a Tracer object. + * + * This hash code is based on object identity. + * This is used in TracerPool to create a hash table of Tracers. + */ + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + /** + * Compare two tracer objects. + * + * Tracer objects are always compared by object equality. + * This is used in TracerPool to create a hash table of Tracers. + */ + @Override + public boolean equals(Object other) { + return (this == other); + } + + @Override + public String toString() { + return "Tracer(" + tracerId + ")"; + } + +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TracerId.java b/htrace-core4/src/main/java/org/apache/htrace/core/TracerId.java new file mode 100644 index 0000000..da482fe --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/TracerId.java @@ -0,0 +1,294 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; +import java.util.Locale; +import java.util.TreeSet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * The HTrace tracer ID.

+ * + * HTrace tracer IDs are created from format strings. + * Format strings contain variables which the TracerId class will + * replace with the correct values at runtime.

+ * + *

    + *
  • %{tname}: the tracer name supplied when creating the Tracer.
  • + *
  • %{pname}: the process name obtained from the JVM.
  • + *
  • %{ip}: will be replaced with an ip address.
  • + *
  • %{pid}: the numerical process ID from the operating system.
  • + *

+ * + * For example, the string "%{pname}/%{ip}" will be replaced with something + * like: DataNode/192.168.0.1, assuming that the process' name is DataNode + * and its IP address is 192.168.0.1.

+ * + * ID strings can contain backslashes as escapes. + * For example, "\a" will map to "a". "\%{ip}" will map to the literal + * string "%{ip}", not the IP address. A backslash itself can be escaped by a + * preceding backslash. + */ +public final class TracerId { + private static final Log LOG = LogFactory.getLog(TracerId.class); + + /** + * The configuration key to use for process id + */ + public static final String TRACER_ID_KEY = "tracer.id"; + + /** + * The default tracer ID to use if no other ID is configured. + */ + private static final String DEFAULT_TRACER_ID = "%{tname}/%{ip}"; + + private final String tracerName; + + private final String tracerId; + + public TracerId(HTraceConfiguration conf, String tracerName) { + this.tracerName = tracerName; + String fmt = conf.get(TRACER_ID_KEY, DEFAULT_TRACER_ID); + StringBuilder bld = new StringBuilder(); + StringBuilder varBld = null; + boolean escaping = false; + int varSeen = 0; + for (int i = 0, len = fmt.length() ; i < len; i++) { + char c = fmt.charAt(i); + if (c == '\\') { + if (!escaping) { + escaping = true; + continue; + } + } + switch (varSeen) { + case 0: + if (c == '%') { + if (!escaping) { + varSeen = 1; + continue; + } + } + escaping = false; + varSeen = 0; + bld.append(c); + break; + case 1: + if (c == '{') { + if (!escaping) { + varSeen = 2; + varBld = new StringBuilder(); + continue; + } + } + escaping = false; + varSeen = 0; + bld.append("%").append(c); + break; + default: + if (c == '}') { + if (!escaping) { + String var = varBld.toString(); + bld.append(processShellVar(var)); + varBld = null; + varSeen = 0; + continue; + } + } + escaping = false; + varBld.append(c); + varSeen++; + break; + } + } + if (varSeen > 0) { + LOG.warn("Unterminated process ID substitution variable at the end " + + "of format string " + fmt); + } + this.tracerId = bld.toString(); + if (LOG.isTraceEnabled()) { + LOG.trace("ProcessID(fmt=" + fmt + "): computed process ID of \"" + + this.tracerId + "\""); + } + } + + private String processShellVar(String var) { + if (var.equals("tname")) { + return tracerName; + } else if (var.equals("pname")) { + return getProcessName(); + } else if (var.equals("ip")) { + return getBestIpString(); + } else if (var.equals("pid")) { + return Long.valueOf(getOsPid()).toString(); + } else { + LOG.warn("unknown ProcessID variable " + var); + return ""; + } + } + + static String getProcessName() { + String cmdLine = System.getProperty("sun.java.command"); + if (cmdLine != null && !cmdLine.isEmpty()) { + String fullClassName = cmdLine.split("\\s+")[0]; + String[] classParts = fullClassName.split("\\."); + cmdLine = classParts[classParts.length - 1]; + } + return (cmdLine == null || cmdLine.isEmpty()) ? "Unknown" : cmdLine; + } + + /** + * Get the best IP address that represents this node.

+ * + * This is complicated since nodes can have multiple network interfaces, + * and each network interface can have multiple IP addresses. What we're + * looking for here is an IP address that will serve to identify this node + * to HTrace. So we prefer site-local addresess (i.e. private ones on the + * LAN) to publicly routable interfaces. If there are multiple addresses + * to choose from, we select the one which comes first in textual sort + * order. This should ensure that we at least consistently call each node + * by a single name. + */ + static String getBestIpString() { + Enumeration ifaces; + try { + ifaces = NetworkInterface.getNetworkInterfaces(); + } catch (SocketException e) { + LOG.error("Error getting network interfaces", e); + return "127.0.0.1"; + } + TreeSet siteLocalCandidates = new TreeSet(); + TreeSet candidates = new TreeSet(); + while (ifaces.hasMoreElements()) { + NetworkInterface iface = ifaces.nextElement(); + for (Enumeration addrs = + iface.getInetAddresses(); addrs.hasMoreElements();) { + InetAddress addr = addrs.nextElement(); + if (!addr.isLoopbackAddress()) { + if (addr.isSiteLocalAddress()) { + siteLocalCandidates.add(addr.getHostAddress()); + } else { + candidates.add(addr.getHostAddress()); + } + } + } + } + if (!siteLocalCandidates.isEmpty()) { + return siteLocalCandidates.first(); + } + if (!candidates.isEmpty()) { + return candidates.first(); + } + return "127.0.0.1"; + } + + /** + * Get the process id from the operating system.

+ * + * Unfortunately, there is no simple method to get the process id in Java. + * The approach we take here is to use the shell method (see + * {TracerId#getOsPidFromShellPpid}) unless we are on Windows, where the + * shell is not available. On Windows, we use + * {TracerId#getOsPidFromManagementFactory}, which depends on some + * undocumented features of the JVM, but which doesn't require a shell. + */ + static long getOsPid() { + if ((System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH)). + contains("windows")) { + return getOsPidFromManagementFactory(); + } else { + return getOsPidFromShellPpid(); + } + } + + /** + * Get the process ID by executing a shell and printing the PPID (parent + * process ID).

+ * + * This method of getting the process ID doesn't depend on any undocumented + * features of the virtual machine, and should work on almost any UNIX + * operating system. + */ + private static long getOsPidFromShellPpid() { + Process p = null; + StringBuilder sb = new StringBuilder(); + try { + p = new ProcessBuilder("/usr/bin/env", "sh", "-c", "echo $PPID"). + redirectErrorStream(true).start(); + BufferedReader reader = new BufferedReader( + new InputStreamReader(p.getInputStream())); + String line = ""; + while ((line = reader.readLine()) != null) { + sb.append(line.trim()); + } + int exitVal = p.waitFor(); + if (exitVal != 0) { + throw new IOException("Process exited with error code " + + Integer.valueOf(exitVal).toString()); + } + } catch (InterruptedException e) { + LOG.error("Interrupted while getting operating system pid from " + + "the shell.", e); + return 0L; + } catch (IOException e) { + LOG.error("Error getting operating system pid from the shell.", e); + return 0L; + } finally { + if (p != null) { + p.destroy(); + } + } + try { + return Long.parseLong(sb.toString()); + } catch (NumberFormatException e) { + LOG.error("Error parsing operating system pid from the shell.", e); + return 0L; + } + } + + /** + * Get the process ID by looking at the name of the managed bean for the + * runtime system of the Java virtual machine.

+ * + * Although this is undocumented, in the Oracle JVM this name is of the form + * [OS_PROCESS_ID]@[HOSTNAME]. + */ + private static long getOsPidFromManagementFactory() { + try { + return Long.parseLong(ManagementFactory.getRuntimeMXBean(). + getName().split("@")[0]); + } catch (NumberFormatException e) { + LOG.error("Failed to get the operating system process ID from the name " + + "of the managed bean for the JVM.", e); + return 0L; + } + } + + public String get() { + return tracerId; + } +} diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TracerPool.java b/htrace-core4/src/main/java/org/apache/htrace/core/TracerPool.java new file mode 100644 index 0000000..26e39f5 --- /dev/null +++ b/htrace-core4/src/main/java/org/apache/htrace/core/TracerPool.java @@ -0,0 +1,285 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.util.Arrays; +import java.util.HashSet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A pool of Tracer objects. + * + * There may be more than one {@link Tracer} running inside a single 'process'; for example, + * unit tests may spin up a DataNode, a NameNode, and HDFS clients all running in a single JVM + * instance, each with its own Tracer. TracerPool is where all Tracer instances register + * on creation so Tracers can coordinate around shared resources such as {@link SpanReceiver} + * instances. TracerPool takes care of properly cleaning up registered Tracer instances on shutdown. + */ +public class TracerPool { + private static final Log LOG = LogFactory.getLog(TracerPool.class); + + /** + * The global pool of tracer objects. + * + * This is the pool that new tracers get put into by default. + */ + static final TracerPool GLOBAL = new TracerPool("Global"); + + /** + * The shutdown hook which closes the Tracers in this pool when the process is + * shutting down. + */ + private class SpanReceiverShutdownHook extends Thread { + SpanReceiverShutdownHook() { + setName("SpanReceiverShutdownHook"); + setDaemon(false); + } + + @Override + public void run() { + removeAndCloseAllSpanReceivers(); + } + } + + /** + * The name of this TracerPool. + */ + private final String name; + + /** + * The current span receivers which these tracers are using. + * + * Can be read locklessly. Must be written under the lock. + * The array itself should never be modified. + */ + private volatile SpanReceiver[] curReceivers; + + /** + * The currently installed shutdown hook, or null if no hook has been + * installed. + */ + private SpanReceiverShutdownHook shutdownHook; + + /** + * The current Tracers. + */ + private final HashSet curTracers; + + /** + * Get the global tracer pool. + */ + public static TracerPool getGlobalTracerPool() { + return GLOBAL; + } + + public TracerPool(String name) { + this.name = name; + this.shutdownHook = null; + this.curTracers = new HashSet(); + this.curReceivers = new SpanReceiver[0]; + } + + /** + * Return the name of this TracerPool. + */ + public String getName() { + return name; + } + + /** + * Returns an array of all the current span receivers. + * + * Note that if the current span receivers change, those changes will not be + * reflected in this array. In other words, this array may be stale. + */ + public SpanReceiver[] getReceivers() { + return curReceivers; + } + + /** + * Add a new span receiver. + * + * @param receiver The new receiver to add. + * + * @return True if the new receiver was added; false if it + * already was there. + */ + public synchronized boolean addReceiver(SpanReceiver receiver) { + SpanReceiver[] receivers = curReceivers; + for (int i = 0; i < receivers.length; i++) { + if (receivers[i] == receiver) { + LOG.trace(toString() + ": can't add receiver " + receiver.toString() + + " since it is already in this pool."); + return false; + } + } + SpanReceiver[] newReceivers = + Arrays.copyOf(receivers, receivers.length + 1); + newReceivers[receivers.length] = receiver; + registerShutdownHookIfNeeded(); + curReceivers = newReceivers; + LOG.trace(toString() + ": added receiver " + receiver.toString()); + return true; + } + + /** + * Register the shutdown hook if needed. + */ + private synchronized void registerShutdownHookIfNeeded() { + if (shutdownHook != null) { + return; + } + shutdownHook = new SpanReceiverShutdownHook(); + Runtime.getRuntime().addShutdownHook(shutdownHook); + LOG.trace(toString() + ": registered shutdown hook."); + } + + /** + * Remove a span receiver. + * + * @param receiver The receiver to remove. + * + * @return True if the receiver was removed; false if it + * did not exist in this pool. + */ + public synchronized boolean removeReceiver(SpanReceiver receiver) { + SpanReceiver[] receivers = curReceivers; + for (int i = 0; i < receivers.length; i++) { + if (receivers[i] == receiver) { + SpanReceiver[] newReceivers = new SpanReceiver[receivers.length - 1]; + System.arraycopy(receivers, 0, newReceivers, 0, i); + System.arraycopy(receivers, i + 1, newReceivers, i, + receivers.length - i - 1); + curReceivers = newReceivers; + LOG.trace(toString() + ": removed receiver " + receiver.toString()); + return true; + } + } + LOG.trace(toString() + ": can't remove receiver " + receiver.toString() + + " since it's not currently in this pool."); + return false; + } + + /** + * Remove and close a span receiver. + * + * @param receiver The receiver to remove. + * + * @return True if the receiver was removed; false if it + * did not exist in this pool. + */ + public boolean removeAndCloseReceiver(SpanReceiver receiver) { + if (!removeReceiver(receiver)) { + return false; + } + try { + LOG.trace(toString() + ": closing receiver " + receiver.toString()); + receiver.close(); + } catch (Throwable t) { + LOG.error(toString() + ": error closing " + receiver.toString(), t); + } + return true; + } + + /** + * Remove and close all of the span receivers. + */ + private synchronized void removeAndCloseAllSpanReceivers() { + SpanReceiver[] receivers = curReceivers; + curReceivers = new SpanReceiver[0]; + for (SpanReceiver receiver : receivers) { + try { + LOG.trace(toString() + ": closing receiver " + receiver.toString()); + receiver.close(); + } catch (Throwable t) { + LOG.error(toString() + ": error closing " + receiver.toString(), t); + } + } + } + + /** + * Given a SpanReceiver class name, return the existing instance of that span + * receiver, if possible; otherwise, invoke the callable to create a new + * instance. + * + * @param className The span receiver class name. + * @param conf The HTrace configuration. + * @param classLoader The class loader to use. + * + * @return The SpanReceiver. + */ + public synchronized SpanReceiver loadReceiverType(String className, + HTraceConfiguration conf, ClassLoader classLoader) { + SpanReceiver[] receivers = curReceivers; + for (SpanReceiver receiver : receivers) { + if (receiver.getClass().getName().equals(className)) { + LOG.trace(toString() + ": returning a reference to receiver " + + receiver.toString()); + return receiver; + } + } + LOG.trace(toString() + ": creating a new SpanReceiver of type " + + className); + SpanReceiver receiver = new SpanReceiver.Builder(conf). + className(className). + classLoader(classLoader). + build(); + addReceiver(receiver); + return receiver; + } + + /** + * Returns an array of all the current Tracers. + * + * Note that if the current Tracers change, those changes will not be + * reflected in this array. In other words, this array may be stale. + */ + public synchronized Tracer[] getTracers() { + return curTracers.toArray(new Tracer[curTracers.size()]); + } + + /** + * Add a new Tracer. + */ + synchronized void addTracer(Tracer tracer) { + if (curTracers.add(tracer)) { + LOG.trace(toString() + ": adding tracer " + tracer.toString()); + } + } + + /** + * Remove a Tracer. + * + * If the Tracer removed was the last one, we will close all the SpanReceiver + * objects that we're managing. + */ + synchronized void removeTracer(Tracer tracer) { + if (curTracers.remove(tracer)) { + LOG.trace(toString() + ": removing tracer " + tracer.toString()); + if (curTracers.size() == 0) { + removeAndCloseAllSpanReceivers(); + } + } + } + + @Override + public String toString() { + return "TracerPool(" + name + ")"; + } +} \ No newline at end of file diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestBadClient.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestBadClient.java new file mode 100644 index 0000000..87ae8e9 --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestBadClient.java @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.io.File; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.containsString; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class TestBadClient { + @After + public void clearBadState() { + // Clear the bad trace state so that we don't disrupt other unit tests + // that run in this JVM. + Tracer.threadLocalScope.set(null); + } + + /** + * Test closing an outer scope when an inner one is still active. + */ + @Test + public void TestClosingOuterScope() throws Exception { + Tracer tracer = new Tracer.Builder(). + name("TestClosingOuterScopeTracer"). + tracerPool(new TracerPool("TestClosingOuterScope")). + conf(HTraceConfiguration. + fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build(); + boolean gotException = false; + TraceScope outerScope = tracer.newScope("outer"); + TraceScope innerScope = tracer.newScope("inner"); + try { + outerScope.close(); + } catch (RuntimeException e) { + assertThat(e.getMessage(), + containsString("it is not the current TraceScope")); + gotException = true; + } + assertTrue("Expected to get exception because of improper " + + "scope closure.", gotException); + innerScope.close(); + tracer.close(); + } + + /** + * Test calling detach() two times on a scope object. + */ + @Test + public void TestDoubleDetachIsCaught() throws Exception { + Tracer tracer = new Tracer.Builder(). + name("TestDoubleDetach"). + tracerPool(new TracerPool("TestDoubleDetachIsCaught")). + conf(HTraceConfiguration. + fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build(); + boolean gotException = false; + TraceScope myScope = tracer.newScope("myScope"); + myScope.detach(); + try { + myScope.detach(); + } catch (RuntimeException e) { + assertThat(e.getMessage(), + containsString("it is already detached.")); + gotException = true; + } + assertTrue("Expected to get exception because of double TraceScope " + + "detach.", gotException); + tracer.close(); + } + + /** + * Test calling detach() two times on a scope object. + */ + @Test + public void TestDoubleDetachOnNullScope() throws Exception { + Tracer tracer = new Tracer.Builder(). + name("TestDoubleDetachOnNullScope"). + tracerPool(new TracerPool("TestDoubleDetachOnNullScope")). + conf(HTraceConfiguration. + fromKeyValuePairs("sampler.classes", "NeverSampler")).build(); + boolean gotException = false; + TraceScope myScope = tracer.newScope("myScope"); + myScope.detach(); + try { + myScope.detach(); + } catch (RuntimeException e) { + assertThat(e.getMessage(), + containsString("it is already detached.")); + gotException = true; + } + assertTrue("Expected to get exception because of double TraceScope " + + "detach on NullScope.", gotException); + tracer.close(); + } + + /** + * Test calling reattach() two times on a scope object. + */ + @Test + public void TestDoubleReattachIsCaught() throws Exception { + Tracer tracer = new Tracer.Builder(). + name("TestDoubleReattach"). + tracerPool(new TracerPool("TestDoubleReattachIsCaught")). + conf(HTraceConfiguration. + fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build(); + boolean gotException = false; + TraceScope myScope = tracer.newScope("myScope"); + myScope.detach(); + myScope.reattach(); + try { + myScope.reattach(); + } catch (RuntimeException e) { + assertThat(e.getMessage(), + containsString("it is not detached.")); + gotException = true; + } + assertTrue("Expected to get exception because of double TraceScope " + + "reattach.", gotException); + tracer.close(); + } + + private static class ScopeHolder { + TraceScope scope; + + void set(TraceScope scope) { + this.scope = scope; + } + } + + /** + * Test correctly passing spans between threads using detach(). + */ + @Test + public void TestPassingSpanBetweenThreads() throws Exception { + final Tracer tracer = new Tracer.Builder(). + name("TestPassingSpanBetweenThreads"). + tracerPool(new TracerPool("TestPassingSpanBetweenThreads")). + conf(HTraceConfiguration. + fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build(); + POJOSpanReceiver receiver = + new POJOSpanReceiver(HTraceConfiguration.EMPTY); + tracer.getTracerPool().addReceiver(receiver); + final ScopeHolder scopeHolder = new ScopeHolder(); + Thread th = new Thread(new Runnable() { + @Override + public void run() { + TraceScope workerScope = tracer.newScope("workerSpan"); + workerScope.detach(); + scopeHolder.set(workerScope); + } + }); + th.start(); + th.join(); + TraceScope workerScope = scopeHolder.scope; + SpanId workerScopeId = workerScope.getSpan().getSpanId(); + + // Create new scope whose parent is the worker thread's span. + workerScope.reattach(); + TraceScope nested = tracer.newScope("nested"); + nested.close(); + // Create another span which also descends from the worker thread's span. + TraceScope nested2 = tracer.newScope("nested2"); + nested2.close(); + + // Close the worker thread's span. + workerScope.close(); + + // We can create another descendant, even though the worker thread's span + // has been stopped. + TraceScope lateChildScope = tracer.newScope("lateChild", workerScopeId); + lateChildScope.close(); + tracer.close(); + + TraceGraph traceGraph = new TraceGraph(receiver.getSpans()); + Collection rootSpans = + traceGraph.getSpansByParent().find(SpanId.INVALID); + Assert.assertEquals(1, rootSpans.size()); + Assert.assertEquals(workerScopeId, + rootSpans.iterator().next().getSpanId()); + Collection childSpans = + traceGraph.getSpansByParent().find(workerScopeId); + Assert.assertEquals(3, childSpans.size()); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestCountSampler.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestCountSampler.java new file mode 100644 index 0000000..e26115d --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestCountSampler.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import org.junit.Assert; +import org.junit.Test; + +public class TestCountSampler { + + @Test + public void testNext() { + CountSampler half = new CountSampler(HTraceConfiguration. + fromKeyValuePairs("sampler.frequency", "2")); + CountSampler hundred = new CountSampler(HTraceConfiguration. + fromKeyValuePairs("sampler.frequency", "100")); + int halfCount = 0; + int hundredCount = 0; + for (int i = 0; i < 200; i++) { + if (half.next()) + halfCount++; + if (hundred.next()) + hundredCount++; + } + Assert.assertEquals(2, hundredCount); + Assert.assertEquals(100, halfCount); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestHTrace.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestHTrace.java new file mode 100644 index 0000000..06ca189 --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestHTrace.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.apache.htrace.core.TraceGraph.SpansByParent; + +import org.junit.Assert; +import org.junit.Test; + +public class TestHTrace { + @Test + public void TestTracerCreateAndClose() throws Exception { + Tracer tracer = new Tracer.Builder(). + name("TestSimpleScope"). + tracerPool(new TracerPool("TestTracerCreateAndClose")). + conf(HTraceConfiguration.fromKeyValuePairs( + "sampler.classes", "AlwaysSampler")). + build(); + POJOSpanReceiver receiver = + new POJOSpanReceiver(HTraceConfiguration.EMPTY); + tracer.getTracerPool().addReceiver(receiver); + tracer.close(); + Assert.assertTrue(receiver.getSpans().isEmpty()); + } + + @Test + public void TestSimpleScope() throws Exception { + Tracer tracer = new Tracer.Builder(). + name("TestSimpleScope"). + tracerPool(new TracerPool("TestSimpleScope")). + conf(HTraceConfiguration.fromKeyValuePairs( + "sampler.classes", "AlwaysSampler")). + build(); + POJOSpanReceiver receiver = + new POJOSpanReceiver(HTraceConfiguration.EMPTY); + tracer.getTracerPool().addReceiver(receiver); + TraceScope scope = tracer.newScope("Foo"); + scope.close(); + tracer.close(); + Assert.assertEquals(1, receiver.getSpans().size()); + Span span = receiver.getSpans().iterator().next(); + Assert.assertEquals(0, span.getParents().length); + } + + @Test + public void TestCreateSpans() throws Exception { + Tracer tracer = new Tracer.Builder(). + name("TestCreateSpans"). + tracerPool(new TracerPool("TestCreateSpans")). + conf(HTraceConfiguration.fromKeyValuePairs( + "sampler.classes", "AlwaysSampler")). + build(); + POJOSpanReceiver receiver = + new POJOSpanReceiver(HTraceConfiguration.EMPTY); + tracer.getTracerPool().addReceiver(receiver); + TraceCreator traceCreator = new TraceCreator(tracer); + traceCreator.createSampleRpcTrace(); + traceCreator.createSimpleTrace(); + traceCreator.createThreadedTrace(); + tracer.close(); + TraceGraph traceGraph = new TraceGraph(receiver.getSpans()); + Collection roots = traceGraph.getSpansByParent().find(SpanId.INVALID); + Assert.assertTrue("Trace tree must have roots", !roots.isEmpty()); + Assert.assertEquals(3, roots.size()); + + Map descriptionToRootSpan = new HashMap(); + for (Span root : roots) { + descriptionToRootSpan.put(root.getDescription(), root); + } + + Assert.assertTrue(descriptionToRootSpan.keySet().contains( + TraceCreator.RPC_TRACE_ROOT)); + Assert.assertTrue(descriptionToRootSpan.keySet().contains( + TraceCreator.SIMPLE_TRACE_ROOT)); + Assert.assertTrue(descriptionToRootSpan.keySet().contains( + TraceCreator.THREADED_TRACE_ROOT)); + + SpansByParent spansByParentId = traceGraph.getSpansByParent(); + + Span rpcTraceRoot = descriptionToRootSpan.get(TraceCreator.RPC_TRACE_ROOT); + Assert.assertEquals(1, spansByParentId.find(rpcTraceRoot.getSpanId()).size()); + + Span rpcTraceChild1 = spansByParentId.find(rpcTraceRoot.getSpanId()) + .iterator().next(); + Assert.assertEquals(1, spansByParentId.find(rpcTraceChild1.getSpanId()).size()); + + Span rpcTraceChild2 = spansByParentId.find(rpcTraceChild1.getSpanId()) + .iterator().next(); + Assert.assertEquals(1, spansByParentId.find(rpcTraceChild2.getSpanId()).size()); + + Span rpcTraceChild3 = spansByParentId.find(rpcTraceChild2.getSpanId()) + .iterator().next(); + Assert.assertEquals(0, spansByParentId.find(rpcTraceChild3.getSpanId()).size()); + } + + @Test(timeout=60000) + public void testRootSpansHaveNonZeroSpanId() throws Exception { + Tracer tracer = new Tracer.Builder(). + name("testRootSpansHaveNonZeroSpanId"). + tracerPool(new TracerPool("testRootSpansHaveNonZeroSpanId")). + conf(HTraceConfiguration.fromKeyValuePairs( + "sampler.classes", "AlwaysSampler")).build(); + TraceScope scope = tracer. + newScope("myRootSpan", new SpanId(100L, 200L)); + Assert.assertNotNull(scope); + Assert.assertEquals("myRootSpan", scope.getSpan().getDescription()); + Assert.assertTrue(scope.getSpan().getSpanId().isValid()); + Assert.assertEquals(100L, scope.getSpan().getSpanId().getHigh()); + Assert.assertNotEquals(0L, scope.getSpan().getSpanId().getLow()); + scope.close(); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java new file mode 100644 index 0000000..7ca897f --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +public class TestHTraceConfiguration { + @Test + public void testGetBoolean() throws Exception { + + Map m = new HashMap(); + m.put("testTrue", " True"); + m.put("testFalse", "falsE "); + HTraceConfiguration configuration = HTraceConfiguration.fromMap(m); + + // Tests for value being there + assertTrue(configuration.getBoolean("testTrue", false)); + assertFalse(configuration.getBoolean("testFalse", true)); + + // Test for absent + assertTrue(configuration.getBoolean("absent", true)); + assertFalse(configuration.getBoolean("absent", false)); + } + + @Test + public void testGetInt() throws Exception { + Map m = new HashMap(); + m.put("a", "100"); + m.put("b", "0"); + m.put("c", "-100"); + m.put("d", "5"); + + HTraceConfiguration configuration = HTraceConfiguration.fromMap(m); + assertEquals(100, configuration.getInt("a", -999)); + assertEquals(0, configuration.getInt("b", -999)); + assertEquals(-100, configuration.getInt("c", -999)); + assertEquals(5, configuration.getInt("d", -999)); + assertEquals(-999, configuration.getInt("absent", -999)); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java new file mode 100644 index 0000000..9388707 --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; + +public class TestLocalFileSpanReceiver { + @Test + public void testUniqueLocalTraceFileName() { + String filename1 = LocalFileSpanReceiver.getUniqueLocalTraceFileName(); + String filename2 = LocalFileSpanReceiver.getUniqueLocalTraceFileName(); + boolean eq = filename1.equals(filename2); + if (System.getProperty("os.name").startsWith("Linux")) { + // ${java.io.tmpdir}/[pid] + assertTrue(eq); + } else { + // ${java.io.tmpdir}/[random UUID] + assertFalse(eq); + } + } + + @Test + public void testWriteToLocalFile() throws IOException { + String traceFileName = LocalFileSpanReceiver.getUniqueLocalTraceFileName(); + Tracer tracer = new Tracer.Builder(). + name("testWriteToLocalFileTracer"). + tracerPool(new TracerPool("testWriteToLocalFile")). + conf(HTraceConfiguration.fromKeyValuePairs( + "sampler.classes", "AlwaysSampler", + "span.receiver.classes", LocalFileSpanReceiver.class.getName(), + "local.file.span.receiver.path", traceFileName, + "tracer.id", "%{tname}")). + build(); + TraceScope scope = tracer.newScope("testWriteToLocalFile"); + scope.close(); + tracer.close(); + + ObjectMapper mapper = new ObjectMapper(); + MilliSpan span = mapper.readValue(new File(traceFileName), MilliSpan.class); + assertEquals("testWriteToLocalFile", span.getDescription()); + assertEquals("testWriteToLocalFileTracer", span.getTracerId()); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestMilliSpan.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestMilliSpan.java new file mode 100644 index 0000000..7ce1fdb --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestMilliSpan.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +public class TestMilliSpan { + private void compareSpans(Span expected, Span got) throws Exception { + assertEquals(expected.getStartTimeMillis(), got.getStartTimeMillis()); + assertEquals(expected.getStopTimeMillis(), got.getStopTimeMillis()); + assertEquals(expected.getDescription(), got.getDescription()); + assertEquals(expected.getSpanId(), got.getSpanId()); + assertEquals(expected.getTracerId(), got.getTracerId()); + assertTrue(Arrays.equals(expected.getParents(), got.getParents())); + Map expectedT = expected.getKVAnnotations(); + Map gotT = got.getKVAnnotations(); + if (expectedT == null) { + assertEquals(null, gotT); + } else { + assertEquals(expectedT.size(), gotT.size()); + for (String key : expectedT.keySet()) { + assertEquals(expectedT.get(key), gotT.get(key)); + } + } + List expectedTimeline = + expected.getTimelineAnnotations(); + List gotTimeline = + got.getTimelineAnnotations(); + if (expectedTimeline == null) { + assertEquals(null, gotTimeline); + } else { + assertEquals(expectedTimeline.size(), gotTimeline.size()); + Iterator iter = gotTimeline.iterator(); + for (TimelineAnnotation expectedAnn : expectedTimeline) { + TimelineAnnotation gotAnn = iter.next(); + assertEquals(expectedAnn.getMessage(), gotAnn.getMessage()); + assertEquals(expectedAnn.getTime(), gotAnn.getTime()); + } + } + } + + @Test + public void testJsonSerialization() throws Exception { + MilliSpan span = new MilliSpan.Builder(). + description("foospan"). + begin(123L). + end(456L). + parents(new SpanId[] { new SpanId(7L, 7L) }). + tracerId("b2404.halxg.com:8080"). + spanId(new SpanId(7L, 8L)). + build(); + String json = span.toJson(); + MilliSpan dspan = MilliSpan.fromJson(json); + compareSpans(span, dspan); + } + + @Test + public void testJsonSerializationWithNegativeLongValue() throws Exception { + MilliSpan span = new MilliSpan.Builder(). + description("foospan"). + begin(-1L). + end(-1L). + parents(new SpanId[] { new SpanId(-1L, -1L) }). + tracerId("b2404.halxg.com:8080"). + spanId(new SpanId(-1L, -2L)). + build(); + String json = span.toJson(); + MilliSpan dspan = MilliSpan.fromJson(json); + compareSpans(span, dspan); + } + + @Test + public void testJsonSerializationWithRandomLongValue() throws Exception { + SpanId parentId = SpanId.fromRandom(); + MilliSpan span = new MilliSpan.Builder(). + description("foospan"). + begin(ThreadLocalRandom.current().nextLong()). + end(ThreadLocalRandom.current().nextLong()). + parents(new SpanId[] { parentId }). + tracerId("b2404.halxg.com:8080"). + spanId(parentId.newChildId()). + build(); + String json = span.toJson(); + MilliSpan dspan = MilliSpan.fromJson(json); + compareSpans(span, dspan); + } + + @Test + public void testJsonSerializationWithOptionalFields() throws Exception { + MilliSpan.Builder builder = new MilliSpan.Builder(). + description("foospan"). + begin(300). + end(400). + parents(new SpanId[] { }). + tracerId("b2408.halxg.com:8080"). + spanId(new SpanId(111111111L, 111111111L)); + Map traceInfo = new HashMap(); + traceInfo.put("abc", "123"); + traceInfo.put("def", "456"); + builder.traceInfo(traceInfo); + List timeline = new LinkedList(); + timeline.add(new TimelineAnnotation(310L, "something happened")); + timeline.add(new TimelineAnnotation(380L, "something else happened")); + timeline.add(new TimelineAnnotation(390L, "more things")); + builder.timeline(timeline); + MilliSpan span = builder.build(); + String json = span.toJson(); + MilliSpan dspan = MilliSpan.fromJson(json); + compareSpans(span, dspan); + } + + @Test + public void testJsonSerializationWithFieldsNotSet() throws Exception { + MilliSpan span = new MilliSpan.Builder().build(); + String json = span.toJson(); + MilliSpan dspan = MilliSpan.fromJson(json); + compareSpans(span, dspan); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestNullScope.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestNullScope.java new file mode 100644 index 0000000..c8ed7f1 --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestNullScope.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import org.junit.Assert; +import org.junit.Test; + +public class TestNullScope { + private void verifyNullScope(TraceScope scope) { + Assert.assertTrue(null == scope.getSpan()); + Assert.assertFalse(scope.detached); + scope.detach(); + Assert.assertTrue(scope.detached); + scope.reattach(); + Assert.assertFalse(scope.detached); + scope.close(); + } + + @Test + public void testNullScope() { + Tracer tracer = new Tracer.Builder(). + name("testNullScope"). + tracerPool(new TracerPool("testNullScope")). + conf(HTraceConfiguration.EMPTY). + build(); + verifyNullScope(tracer.newScope("testNullScope")); + verifyNullScope(tracer.newNullScope()); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestSampler.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestSampler.java new file mode 100644 index 0000000..2305d9f --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestSampler.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +public class TestSampler { + private Sampler[] getSamplersFromConf(HTraceConfiguration conf) { + Tracer tracer = new Tracer.Builder(). + name("MyTracer"). + tracerPool(new TracerPool("getSamplersFromConf")). + conf(conf). + build(); + Sampler[] samplers = tracer.getSamplers(); + tracer.close(); + return samplers; + } + + private void checkArrayContains(List> expected, + Sampler[] samplers) { + for (Iterator> iter = expected.iterator(); + iter.hasNext(); ) { + Class samplerClass = iter.next(); + boolean found = false; + for (int i = 0; i < samplers.length; i++) { + if (samplers[i] != null) { + if (samplers[i].getClass().equals(samplerClass)) { + samplers[i] = null; + found = true; + break; + } + } + } + Assert.assertTrue("Failed to find sampler class " + + samplerClass.getName(), found); + } + for (int i = 0; i < samplers.length; i++) { + if (samplers[i] != null) { + Assert.fail("Got extra sampler of type " + + samplers.getClass().getName()); + } + } + } + + private void checkArrayContains(Class expected, Sampler[] samplers) { + LinkedList> expectedList = + new LinkedList>(); + expectedList.add(expected); + checkArrayContains(expectedList, samplers); + } + + @Test + public void testTracerBuilderCreatesCorrectSamplers() { + Sampler[] samplers = getSamplersFromConf(HTraceConfiguration. + fromKeyValuePairs("sampler.classes", "AlwaysSampler")); + checkArrayContains(AlwaysSampler.class, samplers); + + samplers = getSamplersFromConf(HTraceConfiguration. + fromKeyValuePairs("sampler.classes", "NeverSampler")); + checkArrayContains(NeverSampler.class, samplers); + + samplers = getSamplersFromConf(HTraceConfiguration. + fromKeyValuePairs("sampler.classes", "NonExistentSampler")); + Assert.assertEquals(0, samplers.length); + + samplers = getSamplersFromConf(HTraceConfiguration.EMPTY); + Assert.assertEquals(0, samplers.length); + } + + @Test + public void testAlwaysSampler() { + AlwaysSampler sampler = new AlwaysSampler(HTraceConfiguration.EMPTY); + Assert.assertTrue(sampler.next()); + } + + @Test + public void testNeverSampler() { + NeverSampler sampler = new NeverSampler(HTraceConfiguration.EMPTY); + Assert.assertTrue(!sampler.next()); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanId.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanId.java new file mode 100644 index 0000000..bb57368 --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanId.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.util.Random; + +import org.junit.Assert; +import org.junit.Test; + +public class TestSpanId { + private void testRoundTrip(SpanId id) throws Exception { + String str = id.toString(); + SpanId id2 = SpanId.fromString(str); + Assert.assertEquals(id, id2); + } + + @Test + public void testToStringAndFromString() throws Exception { + testRoundTrip(SpanId.INVALID); + testRoundTrip(new SpanId(0x1234567812345678L, 0x1234567812345678L)); + testRoundTrip(new SpanId(0xf234567812345678L, 0xf234567812345678L)); + testRoundTrip(new SpanId(0xffffffffffffffffL, 0xffffffffffffffffL)); + Random rand = new Random(12345); + for (int i = 0; i < 100; i++) { + testRoundTrip(new SpanId(rand.nextLong(), rand.nextLong())); + } + } + + @Test + public void testValidAndInvalidIds() throws Exception { + Assert.assertFalse(SpanId.INVALID.isValid()); + Assert.assertTrue( + new SpanId(0x1234567812345678L, 0x1234567812345678L).isValid()); + Assert.assertTrue( + new SpanId(0xf234567812345678L, 0xf234567812345678L).isValid()); + } + + private void expectLessThan(SpanId a, SpanId b) throws Exception { + int cmp = a.compareTo(b); + Assert.assertTrue("Expected " + a + " to be less than " + b, + (cmp < 0)); + int cmp2 = b.compareTo(a); + Assert.assertTrue("Expected " + b + " to be greater than " + a, + (cmp2 > 0)); + } + + @Test + public void testIdComparisons() throws Exception { + expectLessThan(new SpanId(0x0000000000000001L, 0x0000000000000001L), + new SpanId(0x0000000000000001L, 0x0000000000000002L)); + expectLessThan(new SpanId(0x0000000000000001L, 0x0000000000000001L), + new SpanId(0x0000000000000002L, 0x0000000000000000L)); + expectLessThan(SpanId.INVALID, + new SpanId(0xffffffffffffffffL, 0xffffffffffffffffL)); + expectLessThan(new SpanId(0x1234567812345678L, 0x1234567812345678L), + new SpanId(0x1234567812345678L, 0xf234567812345678L)); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java new file mode 100644 index 0000000..b97d624 --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +public class TestSpanReceiverBuilder { + private static final Log LOG = + LogFactory.getLog(TestSpanReceiverBuilder.class); + + private List createSpanReceivers(String classes) { + Tracer tracer = new Tracer.Builder(). + name("MyTracer"). + tracerPool(new TracerPool("createSpanReceivers")). + conf(HTraceConfiguration.fromKeyValuePairs( + "span.receiver.classes", classes)). + build(); + SpanReceiver[] receivers = tracer.getTracerPool().getReceivers(); + tracer.close(); + LinkedList receiverList = new LinkedList(); + for (SpanReceiver item: receivers) { + receiverList.add(item); + } + return receiverList; + } + + @Test + public void TestCreateStandardSpanReceivers() { + List receivers; + receivers = createSpanReceivers(""); + Assert.assertTrue(receivers.isEmpty()); + receivers = createSpanReceivers("POJOSpanReceiver"); + Assert.assertTrue(receivers.get(0).getClass().getName(). + equals("org.apache.htrace.core.POJOSpanReceiver")); + receivers = createSpanReceivers( + "org.apache.htrace.core.StandardOutSpanReceiver"); + Assert.assertTrue(receivers.get(0).getClass().getName(). + equals("org.apache.htrace.core.StandardOutSpanReceiver")); + receivers = createSpanReceivers( + "POJOSpanReceiver;StandardOutSpanReceiver"); + Assert.assertEquals(2, receivers.size()); + for (Iterator iter = receivers.iterator(); iter.hasNext();) { + SpanReceiver receiver = iter.next(); + if (receiver.getClass().getName().equals( + "org.apache.htrace.core.POJOSpanReceiver")) { + iter.remove(); + break; + } + } + for (Iterator iter = receivers.iterator(); iter.hasNext();) { + SpanReceiver receiver = iter.next(); + if (receiver.getClass().getName().equals( + "org.apache.htrace.core.StandardOutSpanReceiver")) { + iter.remove(); + break; + } + } + Assert.assertEquals(0, receivers.size()); + } + + public static class GoodSpanReceiver extends SpanReceiver { + public GoodSpanReceiver(HTraceConfiguration conf) { + } + + @Override + public void receiveSpan(Span span) { + } + + @Override + public void close() throws IOException { + } + } + + public static class BadSpanReceiver extends SpanReceiver { + public BadSpanReceiver(HTraceConfiguration conf) { + throw new RuntimeException("Can't create BadSpanReceiver"); + } + + @Override + public void receiveSpan(Span span) { + } + + @Override + public void close() throws IOException { + } + } + + /** + * Test trying to create a SpanReceiver that experiences an error in the + * constructor. + */ + @Test + public void testGetSpanReceiverWithConstructorError() throws Exception { + List receivers; + receivers = createSpanReceivers( + GoodSpanReceiver.class.getName()); + Assert.assertEquals(1, receivers.size()); + Assert.assertTrue(receivers.get(0).getClass().getName(). + contains("GoodSpanReceiver")); + receivers = createSpanReceivers( + BadSpanReceiver.class.getName()); + Assert.assertEquals(0, receivers.size()); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestTracerId.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestTracerId.java new file mode 100644 index 0000000..1e842c5 --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestTracerId.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import org.junit.Test; + +public class TestTracerId { + private void testTracerIdImpl(String expected, String fmt) { + assertEquals(expected, new TracerId( + HTraceConfiguration.fromKeyValuePairs(TracerId.TRACER_ID_KEY, fmt), + "TracerName").get()); + } + + @Test + public void testSimpleTracerIds() { + testTracerIdImpl("abc", "abc"); + testTracerIdImpl("abc", "a\\bc"); + testTracerIdImpl("abc", "ab\\c"); + testTracerIdImpl("abc", "\\a\\b\\c"); + testTracerIdImpl("a\\bc", "a\\\\bc"); + } + + @Test + public void testSubstitutionVariables() throws IOException { + testTracerIdImpl("myTracerName", "my%{tname}"); + testTracerIdImpl(TracerId.getProcessName(), "%{pname}"); + testTracerIdImpl("my." + TracerId.getProcessName(), "my.%{pname}"); + testTracerIdImpl(TracerId.getBestIpString() + ".str", "%{ip}.str"); + testTracerIdImpl("%{pname}", "\\%{pname}"); + testTracerIdImpl("%cash%money{}", "%cash%money{}"); + testTracerIdImpl("Foo." + Long.valueOf(TracerId.getOsPid()).toString(), + "Foo.%{pid}"); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TraceCreator.java b/htrace-core4/src/test/java/org/apache/htrace/core/TraceCreator.java new file mode 100644 index 0000000..b843999 --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TraceCreator.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Does some stuff and traces it. + */ +public class TraceCreator { + public static final String RPC_TRACE_ROOT = "createSampleRpcTrace"; + public static final String THREADED_TRACE_ROOT = "createThreadedTrace"; + public static final String SIMPLE_TRACE_ROOT = "createSimpleTrace"; + + private final Tracer tracer; + + public TraceCreator(Tracer tracer) { + this.tracer = tracer; + } + + public void createSampleRpcTrace() { + TraceScope s = tracer.newScope(RPC_TRACE_ROOT); + try { + pretendRpcSend(); + } finally { + s.close(); + } + } + + public void createSimpleTrace() { + TraceScope s = tracer.newScope(SIMPLE_TRACE_ROOT); + try { + importantWork1(); + } finally { + s.close(); + } + } + + /** + * Creates the demo trace (will create different traces from call to call). + */ + public void createThreadedTrace() { + TraceScope s = tracer.newScope(THREADED_TRACE_ROOT); + try { + Random r = ThreadLocalRandom.current(); + int numThreads = r.nextInt(4) + 1; + Thread[] threads = new Thread[numThreads]; + + for (int i = 0; i < numThreads; i++) { + threads[i] = new Thread(tracer.wrap(new MyRunnable(), null)); + } + for (int i = 0; i < numThreads; i++) { + threads[i].start(); + } + for (int i = 0; i < numThreads; i++) { + try { + threads[i].join(); + } catch (InterruptedException e) { + } + } + importantWork1(); + } finally { + s.close(); + } + } + + private void importantWork1() { + TraceScope cur = tracer.newScope("important work 1"); + try { + Thread.sleep((long) (2000 * Math.random())); + importantWork2(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + cur.close(); + } + } + + private void importantWork2() { + TraceScope cur = tracer.newScope("important work 2"); + try { + Thread.sleep((long) (2000 * Math.random())); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + cur.close(); + } + } + + private class MyRunnable implements Runnable { + @Override + public void run() { + try { + Thread.sleep(750); + Random r = ThreadLocalRandom.current(); + int importantNumber = 100 / r.nextInt(3); + System.out.println("Important number: " + importantNumber); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } catch (ArithmeticException ae) { + TraceScope c = tracer.newScope("dealing with arithmetic exception."); + try { + Thread.sleep((long) (3000 * Math.random())); + } catch (InterruptedException ie1) { + Thread.currentThread().interrupt(); + } finally { + c.close(); + } + } + } + } + + public void pretendRpcSend() { + Span span = tracer.getCurrentSpan(); + pretendRpcReceiveWithTraceInfo(span.getSpanId()); + } + + public void pretendRpcReceiveWithTraceInfo(SpanId parentId) { + TraceScope s = tracer.newScope("received RPC", parentId); + try { + importantWork1(); + } finally { + s.close(); + } + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TraceGraph.java b/htrace-core4/src/test/java/org/apache/htrace/core/TraceGraph.java new file mode 100644 index 0000000..a06e620 --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/core/TraceGraph.java @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.core; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.TreeSet; + +/** + * Used to create the graph formed by spans. + */ +public class TraceGraph { + private static final Log LOG = LogFactory.getLog(Tracer.class); + + + public static class SpansByParent { + /** + * Compare two spans by span ID. + */ + private static Comparator COMPARATOR = + new Comparator() { + @Override + public int compare(Span a, Span b) { + return a.getSpanId().compareTo(b.getSpanId()); + } + }; + + private final TreeSet treeSet; + + private final HashMap> parentToSpans; + + SpansByParent(Collection spans) { + TreeSet treeSet = new TreeSet(COMPARATOR); + parentToSpans = new HashMap>(); + for (Span span : spans) { + treeSet.add(span); + for (SpanId parent : span.getParents()) { + LinkedList list = parentToSpans.get(parent); + if (list == null) { + list = new LinkedList(); + parentToSpans.put(parent, list); + } + list.add(span); + } + if (span.getParents().length == 0) { + LinkedList list = parentToSpans.get(SpanId.INVALID); + if (list == null) { + list = new LinkedList(); + parentToSpans.put(SpanId.INVALID, list); + } + list.add(span); + } + } + this.treeSet = treeSet; + } + + public List find(SpanId parentId) { + LinkedList spans = parentToSpans.get(parentId); + if (spans == null) { + return new LinkedList(); + } + return spans; + } + + public Iterator iterator() { + return Collections.unmodifiableSortedSet(treeSet).iterator(); + } + } + + public static class SpansByTracerId { + /** + * Compare two spans by process ID, and then by span ID. + */ + private static Comparator COMPARATOR = + new Comparator() { + @Override + public int compare(Span a, Span b) { + int cmp = a.getTracerId().compareTo(b.getTracerId()); + if (cmp != 0) { + return cmp; + } + return a.getSpanId().compareTo(b.getSpanId()); + } + }; + + private final TreeSet treeSet; + + SpansByTracerId(Collection spans) { + TreeSet treeSet = new TreeSet(COMPARATOR); + for (Span span : spans) { + treeSet.add(span); + } + this.treeSet = treeSet; + } + + public List find(String tracerId) { + List spans = new ArrayList(); + Span span = new MilliSpan.Builder(). + spanId(SpanId.INVALID). + tracerId(tracerId). + build(); + while (true) { + span = treeSet.higher(span); + if (span == null) { + break; + } + if (span.getTracerId().equals(tracerId)) { + break; + } + spans.add(span); + } + return spans; + } + + public Iterator iterator() { + return Collections.unmodifiableSortedSet(treeSet).iterator(); + } + } + + private final SpansByParent spansByParent; + private final SpansByTracerId spansByTracerId; + + /** + * Create a new TraceGraph + * + * @param spans The collection of spans to use to create this TraceGraph. Should + * have at least one root span. + */ + public TraceGraph(Collection spans) { + this.spansByParent = new SpansByParent(spans); + this.spansByTracerId = new SpansByTracerId(spans); + } + + public SpansByParent getSpansByParent() { + return spansByParent; + } + + public SpansByTracerId getSpansByTracerId() { + return spansByTracerId; + } + + @Override + public String toString() { + StringBuilder bld = new StringBuilder(); + String prefix = ""; + for (Iterator iter = spansByParent.iterator(); iter.hasNext();) { + Span span = iter.next(); + bld.append(prefix).append(span.toString()); + prefix = "\n"; + } + return bld.toString(); + } +} diff --git a/htrace-core4/src/test/java/org/apache/htrace/util/TestUtil.java b/htrace-core4/src/test/java/org/apache/htrace/util/TestUtil.java new file mode 100644 index 0000000..7cb4aed --- /dev/null +++ b/htrace-core4/src/test/java/org/apache/htrace/util/TestUtil.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.util; + +import java.io.File; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Utilities for writing unit tests. + */ +public class TestUtil { + /** + * Get a dump of the stack traces of all threads. + */ + public static String threadDump() { + StringBuilder dump = new StringBuilder(); + Map stackTraces = Thread.getAllStackTraces(); + for (Map.Entry e : stackTraces.entrySet()) { + Thread thread = e.getKey(); + dump.append(String.format( + "\"%s\" %s prio=%d tid=%d %s\njava.lang.Thread.State: %s", + thread.getName(), + (thread.isDaemon() ? "daemon" : ""), + thread.getPriority(), + thread.getId(), + Thread.State.WAITING.equals(thread.getState()) ? + "in Object.wait()" : thread.getState().name().toLowerCase(), + Thread.State.WAITING.equals(thread.getState()) ? + "WAITING (on object monitor)" : thread.getState())); + for (StackTraceElement stackTraceElement : e.getValue()) { + dump.append("\n at "); + dump.append(stackTraceElement); + } + dump.append("\n"); + } + return dump.toString(); + } + + /** + * A callback which returns a value of type T. + * + * TODO: remove this when we're on Java 8, in favor of + * java.util.function.Supplier. + */ + public interface Supplier { + T get(); + } + + /** + * Wait for a condition to become true for a configurable amount of time. + * + * @param check The condition to wait for. + * @param periodMs How often to check the condition, in milliseconds. + * @param timeoutMs How long to wait in total, in milliseconds. + */ + public static void waitFor(Supplier check, + long periodMs, long timeoutMs) + throws TimeoutException, InterruptedException + { + long endNs = System.nanoTime() + + TimeUnit.NANOSECONDS.convert(timeoutMs, TimeUnit.MILLISECONDS); + while (true) { + boolean result = check.get(); + if (result) { + return; + } + long nowNs = System.nanoTime(); + if (nowNs >= endNs) { + throw new TimeoutException("Timed out waiting for test condition. " + + "Thread dump:\n" + threadDump()); + } + Thread.sleep(periodMs); + } + } +} diff --git a/pom.xml b/pom.xml index 0e0342f..42e19ca 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ language governing permissions and limitations under the License. --> htrace-c - htrace-core + htrace-core4 htrace-webapp htrace-zipkin htrace-hbase