Repository: incubator-htrace Updated Branches: refs/heads/master d8409ef9a -> 3c20489f8
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestTracerId.java ---------------------------------------------------------------------- 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}"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TraceCreator.java ---------------------------------------------------------------------- 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(); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TraceGraph.java ---------------------------------------------------------------------- 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<Span> COMPARATOR = + new Comparator<Span>() { + @Override + public int compare(Span a, Span b) { + return a.getSpanId().compareTo(b.getSpanId()); + } + }; + + private final TreeSet<Span> treeSet; + + private final HashMap<SpanId, LinkedList<Span>> parentToSpans; + + SpansByParent(Collection<Span> spans) { + TreeSet<Span> treeSet = new TreeSet<Span>(COMPARATOR); + parentToSpans = new HashMap<SpanId, LinkedList<Span>>(); + for (Span span : spans) { + treeSet.add(span); + for (SpanId parent : span.getParents()) { + LinkedList<Span> list = parentToSpans.get(parent); + if (list == null) { + list = new LinkedList<Span>(); + parentToSpans.put(parent, list); + } + list.add(span); + } + if (span.getParents().length == 0) { + LinkedList<Span> list = parentToSpans.get(SpanId.INVALID); + if (list == null) { + list = new LinkedList<Span>(); + parentToSpans.put(SpanId.INVALID, list); + } + list.add(span); + } + } + this.treeSet = treeSet; + } + + public List<Span> find(SpanId parentId) { + LinkedList<Span> spans = parentToSpans.get(parentId); + if (spans == null) { + return new LinkedList<Span>(); + } + return spans; + } + + public Iterator<Span> iterator() { + return Collections.unmodifiableSortedSet(treeSet).iterator(); + } + } + + public static class SpansByTracerId { + /** + * Compare two spans by process ID, and then by span ID. + */ + private static Comparator<Span> COMPARATOR = + new Comparator<Span>() { + @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<Span> treeSet; + + SpansByTracerId(Collection<Span> spans) { + TreeSet<Span> treeSet = new TreeSet<Span>(COMPARATOR); + for (Span span : spans) { + treeSet.add(span); + } + this.treeSet = treeSet; + } + + public List<Span> find(String tracerId) { + List<Span> spans = new ArrayList<Span>(); + 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<Span> 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<Span> 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<Span> iter = spansByParent.iterator(); iter.hasNext();) { + Span span = iter.next(); + bld.append(prefix).append(span.toString()); + prefix = "\n"; + } + return bld.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/util/TestUtil.java ---------------------------------------------------------------------- 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<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces(); + for (Map.Entry<Thread, StackTraceElement[]> 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> { + 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<Boolean> 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); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/pom.xml ---------------------------------------------------------------------- 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. --> <modules> <module>htrace-c</module> - <module>htrace-core</module> + <module>htrace-core4</module> <module>htrace-webapp</module> <module>htrace-zipkin</module> <module>htrace-hbase</module>
