diff --git a/.travis.yml b/.travis.yml index fa67247..8671fbe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,8 @@ sudo: required language: java -# This project is aimed to be JRE7-compatible. However, there -# are issues testing against oraclejdk7 in Travis: -# https://github.com/travis-ci/travis-ci/issues/7884 +# This project is JRE8-compatible. jdk: - - openjdk7 - openjdk8 - oraclejdk8 diff --git a/DEVELOPER.md b/DEVELOPER.md index 8be7c8e..dc597e3 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -6,7 +6,7 @@ This document summarizes information relevant to Java tally contributors. ### Prerequisites In order to build this project, you must have: -- JDK-7 or later +- JDK-8 or later - Apache Thrift 0.9.x -- only if you plan to make changes to Thrift files and recompile (regenerate) source files ### Building and testing diff --git a/build.gradle b/build.gradle index a5634ab..ded74ec 100644 --- a/build.gradle +++ b/build.gradle @@ -52,8 +52,8 @@ allprojects { apply plugin: 'signing' apply plugin: 'jacoco' - sourceCompatibility = 1.7 - targetCompatibility = 1.7 + sourceCompatibility = 1.8 + targetCompatibility = 1.8 repositories { mavenCentral() diff --git a/core/src/main/java/com/uber/m3/tally/AbstractBuckets.java b/core/src/main/java/com/uber/m3/tally/AbstractBuckets.java index fdd60db..e75130d 100644 --- a/core/src/main/java/com/uber/m3/tally/AbstractBuckets.java +++ b/core/src/main/java/com/uber/m3/tally/AbstractBuckets.java @@ -32,10 +32,10 @@ /** * Abstract {@link Buckets} implementation for common functionality. */ -public abstract class AbstractBuckets implements Buckets { - protected List buckets; +public abstract class AbstractBuckets implements Buckets { + protected List buckets; - AbstractBuckets(T[] buckets) { + AbstractBuckets(E[] buckets) { if (buckets == null) { this.buckets = new ArrayList<>(); } else { @@ -74,7 +74,7 @@ public boolean contains(Object o) { } @Override - public Iterator iterator() { + public Iterator iterator() { return buckets.iterator(); } @@ -84,7 +84,7 @@ public Object[] toArray() { } @Override - public boolean add(T o) { + public boolean add(E o) { return buckets.add(o); } @@ -94,12 +94,12 @@ public boolean remove(Object o) { } @Override - public boolean addAll(Collection c) { + public boolean addAll(Collection c) { return buckets.addAll(c); } @Override - public boolean addAll(int index, Collection c) { + public boolean addAll(int index, Collection c) { return buckets.addAll(index, c); } @@ -109,22 +109,22 @@ public void clear() { } @Override - public T get(int index) { + public E get(int index) { return buckets.get(index); } @Override - public T set(int index, T element) { + public E set(int index, E element) { return buckets.set(index, element); } @Override - public void add(int index, T element) { + public void add(int index, E element) { buckets.add(index, element); } @Override - public T remove(int index) { + public E remove(int index) { return buckets.remove(index); } @@ -139,27 +139,27 @@ public int lastIndexOf(Object o) { } @Override - public ListIterator listIterator() { + public ListIterator listIterator() { return buckets.listIterator(); } @Override - public ListIterator listIterator(int index) { + public ListIterator listIterator(int index) { return buckets.listIterator(index); } @Override - public List subList(int fromIndex, int toIndex) { + public List subList(int fromIndex, int toIndex) { return buckets.subList(fromIndex, toIndex); } @Override - public boolean retainAll(Collection c) { + public boolean retainAll(Collection c) { return buckets.retainAll(c); } @Override - public boolean removeAll(Collection c) { + public boolean removeAll(Collection c) { return buckets.removeAll(c); } @@ -169,7 +169,7 @@ public boolean containsAll(Collection c) { } @Override - public Object[] toArray(Object[] a) { + public T[] toArray(T[] a) { return buckets.toArray(a); } diff --git a/core/src/main/java/com/uber/m3/tally/BucketPair.java b/core/src/main/java/com/uber/m3/tally/BucketPair.java index 8efc5f7..ae984e8 100644 --- a/core/src/main/java/com/uber/m3/tally/BucketPair.java +++ b/core/src/main/java/com/uber/m3/tally/BucketPair.java @@ -22,11 +22,66 @@ import com.uber.m3.util.Duration; +import java.util.Collections; + /** * A BucketPair describes the lower and upper bounds * for a derived bucket from a buckets set. */ public interface BucketPair { + + static BucketPair[] create(Buckets buckets) { + if (buckets == null || buckets.size() < 1) { + return new BucketPair[]{ + new BucketPairImpl( + -Double.MAX_VALUE, + Double.MAX_VALUE, + Duration.MIN_VALUE, + Duration.MAX_VALUE + ) + }; + } + + Collections.sort(buckets); + + Double[] asValueBuckets = buckets.asValues(); + Duration[] asDurationBuckets = buckets.asDurations(); + BucketPair[] pairs = new BucketPair[buckets.size() + 1]; + + // Add lower bound + pairs[0] = new BucketPairImpl( + -Double.MAX_VALUE, + asValueBuckets[0], + Duration.MIN_VALUE, + asDurationBuckets[0] + ); + + double prevValueBucket = asValueBuckets[0]; + Duration prevDurationBucket = asDurationBuckets[0]; + + for (int i = 1; i < buckets.size(); i++) { + pairs[i] = new BucketPairImpl( + prevValueBucket, + asValueBuckets[i], + prevDurationBucket, + asDurationBuckets[i] + ); + + prevValueBucket = asValueBuckets[i]; + prevDurationBucket = asDurationBuckets[i]; + } + + // Add upper bound + pairs[pairs.length - 1] = new BucketPairImpl( + prevValueBucket, + Double.MAX_VALUE, + prevDurationBucket, + Duration.MAX_VALUE + ); + + return pairs; + } + /** * Returns the lower bound as a {@code double} * @return the lower bound as a {@code double} diff --git a/core/src/main/java/com/uber/m3/tally/BucketPairImpl.java b/core/src/main/java/com/uber/m3/tally/BucketPairImpl.java index b614fec..ab2882f 100644 --- a/core/src/main/java/com/uber/m3/tally/BucketPairImpl.java +++ b/core/src/main/java/com/uber/m3/tally/BucketPairImpl.java @@ -22,18 +22,16 @@ import com.uber.m3.util.Duration; -import java.util.Collections; - /** * Default implementation of a {@link BucketPair} */ -public class BucketPairImpl implements BucketPair { +class BucketPairImpl implements BucketPair { private double lowerBoundValue; private double upperBoundValue; private Duration lowerBoundDuration; private Duration upperBoundDuration; - public BucketPairImpl( + BucketPairImpl( double lowerBoundValue, double upperBoundValue, Duration lowerBoundDuration, @@ -45,58 +43,6 @@ public BucketPairImpl( this.upperBoundDuration = upperBoundDuration; } - public static BucketPair[] bucketPairs(Buckets buckets) { - if (buckets == null || buckets.size() < 1) { - return new BucketPair[]{ - new BucketPairImpl( - -Double.MAX_VALUE, - Double.MAX_VALUE, - Duration.MIN_VALUE, - Duration.MAX_VALUE - ) - }; - } - - Collections.sort(buckets); - - Double[] asValueBuckets = buckets.asValues(); - Duration[] asDurationBuckets = buckets.asDurations(); - BucketPair[] pairs = new BucketPair[buckets.size() + 1]; - - // Add lower bound - pairs[0] = new BucketPairImpl( - -Double.MAX_VALUE, - asValueBuckets[0], - Duration.MIN_VALUE, - asDurationBuckets[0] - ); - - double prevValueBucket = asValueBuckets[0]; - Duration prevDurationBucket = asDurationBuckets[0]; - - for (int i = 1; i < buckets.size(); i++) { - pairs[i] = new BucketPairImpl( - prevValueBucket, - asValueBuckets[i], - prevDurationBucket, - asDurationBuckets[i] - ); - - prevValueBucket = asValueBuckets[i]; - prevDurationBucket = asDurationBuckets[i]; - } - - // Add upper bound - pairs[pairs.length - 1] = new BucketPairImpl( - prevValueBucket, - Double.MAX_VALUE, - prevDurationBucket, - Duration.MAX_VALUE - ); - - return pairs; - } - @Override public double lowerBoundValue() { return lowerBoundValue; diff --git a/core/src/main/java/com/uber/m3/tally/CapableOf.java b/core/src/main/java/com/uber/m3/tally/CapableOf.java index cb554c6..0feaf9b 100644 --- a/core/src/main/java/com/uber/m3/tally/CapableOf.java +++ b/core/src/main/java/com/uber/m3/tally/CapableOf.java @@ -69,8 +69,8 @@ public boolean equals(Object other) { public int hashCode() { int code = 0; - code = 31 * code + new Boolean(reporting).hashCode(); - code = 31 * code + new Boolean(tagging).hashCode(); + code = 31 * code + Boolean.valueOf(reporting).hashCode(); + code = 31 * code + Boolean.valueOf(tagging).hashCode(); return code; } diff --git a/core/src/main/java/com/uber/m3/tally/HistogramImpl.java b/core/src/main/java/com/uber/m3/tally/HistogramImpl.java index 48811e7..1cf4957 100644 --- a/core/src/main/java/com/uber/m3/tally/HistogramImpl.java +++ b/core/src/main/java/com/uber/m3/tally/HistogramImpl.java @@ -53,7 +53,7 @@ class HistogramImpl implements Histogram, StopwatchRecorder { type = Type.VALUE; } - BucketPair[] pairs = BucketPairImpl.bucketPairs(buckets); + BucketPair[] pairs = BucketPair.create(buckets); int pairsLen = pairs.length; this.name = name; @@ -174,7 +174,7 @@ public void recordStopwatch(long stopwatchStart) { recordDuration(Duration.between(stopwatchStart, System.nanoTime())); } - class HistogramBucket { + static class HistogramBucket { CounterImpl samples; double valueLowerBound; double valueUpperBound; diff --git a/core/src/main/java/com/uber/m3/tally/NoopScope.java b/core/src/main/java/com/uber/m3/tally/NoopScope.java new file mode 100644 index 0000000..d2854a4 --- /dev/null +++ b/core/src/main/java/com/uber/m3/tally/NoopScope.java @@ -0,0 +1,38 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally; + +import com.uber.m3.util.Duration; + +/** + * NoopScope is scope that does not report stats. + */ +public class NoopScope { + + /** + * Creates a new NoopScope. + */ + public static Scope create() { + return new RootScopeBuilder() + .reporter(new NullStatsReporter()) + .reportEvery(Duration.ZERO); + } +} diff --git a/core/src/main/java/com/uber/m3/tally/NullStatsReporter.java b/core/src/main/java/com/uber/m3/tally/NullStatsReporter.java new file mode 100644 index 0000000..29f029e --- /dev/null +++ b/core/src/main/java/com/uber/m3/tally/NullStatsReporter.java @@ -0,0 +1,83 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally; + +import com.uber.m3.util.Duration; + +import java.util.Map; + +/** + * NullStatsReporter is an implementation of StatsReporter that simply does nothing. + */ +public class NullStatsReporter implements StatsReporter { + @Override + public void reportCounter(String name, Map tags, long value) { + // noop + } + + @Override + public void reportGauge(String name, Map tags, double value) { + // noop + } + + @Override + public void reportTimer(String name, Map tags, Duration interval) { + // noop + } + + @Override + public void reportHistogramValueSamples( + String name, Map tags, + Buckets buckets, + double bucketLowerBound, + double bucketUpperBound, + long samples + ) { + // noop + } + + @Override + public void reportHistogramDurationSamples( + String name, + Map tags, + Buckets buckets, + Duration bucketLowerBound, + Duration bucketUpperBound, long samples + ) { + // noop + } + + @Override + public Capabilities capabilities() { + return CapableOf.NONE; + } + + @Override + public void flush() { + // noop + } + + @Override + public void close() { + // noop + } +} diff --git a/core/src/main/java/com/uber/m3/tally/ScopeBuilder.java b/core/src/main/java/com/uber/m3/tally/ScopeBuilder.java index 09a00ff..cfda723 100644 --- a/core/src/main/java/com/uber/m3/tally/ScopeBuilder.java +++ b/core/src/main/java/com/uber/m3/tally/ScopeBuilder.java @@ -20,6 +20,8 @@ package com.uber.m3.tally; +import com.uber.m3.tally.sanitizers.Sanitizer; +import com.uber.m3.tally.sanitizers.SanitizerBuilder; import com.uber.m3.util.Duration; import com.uber.m3.util.ImmutableMap; @@ -32,7 +34,7 @@ */ public class ScopeBuilder { private static final String DEFAULT_SEPARATOR = "."; - private static final Buckets DEFAULT_SCOPE_BUCKETS = new DurationBuckets(new Duration[] { + private static final Buckets DEFAULT_SCOPE_BUCKETS = new DurationBuckets(new Duration[]{ Duration.ZERO, Duration.ofMillis(10), Duration.ofMillis(15), @@ -55,6 +57,7 @@ public class ScopeBuilder { protected String separator = DEFAULT_SEPARATOR; protected ImmutableMap tags; protected Buckets defaultBuckets = DEFAULT_SCOPE_BUCKETS; + protected Sanitizer sanitizer = new SanitizerBuilder().build(); private ScheduledExecutorService scheduler; private ScopeImpl.Registry registry; @@ -67,7 +70,8 @@ protected ScopeBuilder(ScheduledExecutorService scheduler, ScopeImpl.Registry re } /** - * Update the reporter + * Update the reporter. + * * @param reporter value to update to * @return Builder with new param updated */ @@ -77,7 +81,8 @@ public ScopeBuilder reporter(StatsReporter reporter) { } /** - * Update the prefix + * Update the prefix. + * * @param prefix value to update to * @return Builder with new param updated */ @@ -87,7 +92,8 @@ public ScopeBuilder prefix(String prefix) { } /** - * Update the separator + * Update the separator. + * * @param separator value to update to * @return Builder with new param updated */ @@ -97,7 +103,8 @@ public ScopeBuilder separator(String separator) { } /** - * Update the tags, cloning the tags map to an ImmutableMap + * Update the tags, cloning the tags map to an ImmutableMap. + * * @param tags value to update to * @return Builder with new param updated */ @@ -108,7 +115,8 @@ public ScopeBuilder tags(Map tags) { } /** - * Update the tags. Since this function takes an ImmutableMap, we don't need to clone it + * Update the tags. Since this function takes an ImmutableMap, we don't need to clone it. + * * @param tags value to update to * @return Builder with new param updated */ @@ -119,7 +127,8 @@ public ScopeBuilder tags(ImmutableMap tags) { } /** - * Update the defaultBuckets + * Update the defaultBuckets. + * * @param defaultBuckets value to update to * @return Builder with new param updated */ @@ -128,6 +137,17 @@ public ScopeBuilder defaultBuckets(Buckets defaultBuckets) { return this; } + /** + * Update the sanitizer. + * + * @param sanitizer value to update to + * @return Builder with new param updated + */ + public ScopeBuilder sanitizer(Sanitizer sanitizer) { + this.sanitizer = sanitizer; + return this; + } + // Private build method - clients should rely on `reportEvery` to create root scopes, and // a root scope's `tagged` and `subScope` functions to create subscopes. ScopeImpl build() { @@ -135,7 +155,8 @@ ScopeImpl build() { } /** - * Creates a root scope and starts reporting with the specified interval + * Creates a root scope and starts reporting with the specified interval. + * * @param interval duration between each report * @return the root scope created */ @@ -144,22 +165,28 @@ public Scope reportEvery(Duration interval) { } /** - * Creates a root scope and starts reporting with the specified interval - * @param interval duration between each report + * Creates a root scope and starts reporting with the specified interval. + * + * @param interval duration between each report * @param uncaughtExceptionHandler an {@link java.lang.Thread.UncaughtExceptionHandler} that's * called when there's an uncaught exception in the report loop * @return the root scope created */ - public Scope reportEvery(Duration interval, - Thread.UncaughtExceptionHandler uncaughtExceptionHandler) { - if (interval.compareTo(Duration.ZERO) <= 0) { - throw new IllegalArgumentException("Reporting interval must be a positive Duration"); + public Scope reportEvery( + Duration interval, + Thread.UncaughtExceptionHandler uncaughtExceptionHandler + ) { + if (interval.getNanos() < 0) { + throw new IllegalArgumentException("Reporting interval must be a non-negative Duration"); } ScopeImpl scope = build(); registry.subscopes.put(ScopeImpl.keyForPrefixedStringMap(prefix, tags), scope); - scheduler.scheduleWithFixedDelay(scope.new ReportLoop(uncaughtExceptionHandler), 0, interval.toMillis(), TimeUnit.MILLISECONDS); + if (interval.getNanos() > 0) { + scheduler.scheduleWithFixedDelay( + scope.new ReportLoop(uncaughtExceptionHandler), 0, interval.toMillis(), TimeUnit.MILLISECONDS); + } return scope; } diff --git a/core/src/main/java/com/uber/m3/tally/ScopeImpl.java b/core/src/main/java/com/uber/m3/tally/ScopeImpl.java index 86fb667..2f7a959 100644 --- a/core/src/main/java/com/uber/m3/tally/ScopeImpl.java +++ b/core/src/main/java/com/uber/m3/tally/ScopeImpl.java @@ -20,6 +20,7 @@ package com.uber.m3.tally; +import com.uber.m3.tally.sanitizers.Sanitizer; import com.uber.m3.util.ImmutableMap; import java.util.Arrays; @@ -32,15 +33,16 @@ /** * Default {@link Scope} implementation. */ -class ScopeImpl implements Scope { - private StatsReporter reporter; - private String prefix; - private String separator; - private ImmutableMap tags; - private Buckets defaultBuckets; +class ScopeImpl implements Scope, TestScope { + private final StatsReporter reporter; + private final String prefix; + private final String separator; + private final ImmutableMap tags; + private final Buckets defaultBuckets; + private final Sanitizer sanitizer; - private ScheduledExecutorService scheduler; - private Registry registry; + private final ScheduledExecutorService scheduler; + private final Registry registry; // ConcurrentHashMap nearly always allowing read operations seems like a good // performance upside to the consequence of reporting a newly-made metric in @@ -56,72 +58,91 @@ class ScopeImpl implements Scope { this.scheduler = scheduler; this.registry = registry; + this.sanitizer = builder.sanitizer; this.reporter = builder.reporter; - this.prefix = builder.prefix; - this.separator = builder.separator; - this.tags = builder.tags; + this.prefix = this.sanitizer.name(builder.prefix); + this.separator = this.sanitizer.name(builder.separator); + this.tags = copyAndSanitizeMap(builder.tags); this.defaultBuckets = builder.defaultBuckets; } + // Serializes a map to generate a key for a prefix/map combination + // Non-generic EMPTY ImmutableMap will never contain any elements + @SuppressWarnings("unchecked") + static String keyForPrefixedStringMap(String prefix, ImmutableMap stringMap) { + if (prefix == null) { + prefix = ""; + } + + if (stringMap == null) { + stringMap = ImmutableMap.EMPTY; + } + + Set keySet = stringMap.keySet(); + String[] sortedKeys = keySet.toArray(new String[keySet.size()]); + Arrays.sort(sortedKeys); + + StringBuilder keyBuffer = new StringBuilder(prefix.length() + sortedKeys.length * 20); + keyBuffer.append(prefix); + keyBuffer.append("+"); + + for (int i = 0; i < sortedKeys.length; i++) { + keyBuffer.append(sortedKeys[i]); + keyBuffer.append("="); + keyBuffer.append(stringMap.get(sortedKeys[i])); + + if (i != sortedKeys.length - 1) { + keyBuffer.append(","); + } + } + + return keyBuffer.toString(); + } + @Override public Counter counter(String name) { + name = sanitizer.name(name); CounterImpl counter = counters.get(name); if (counter != null) { return counter; } - synchronized (counters) { - if (!counters.containsKey(name)) { - counters.put(name, new CounterImpl()); - } - - counter = counters.get(name); - } - - return counter; + counters.putIfAbsent(name, new CounterImpl()); + return counters.get(name); } @Override public Gauge gauge(String name) { + name = sanitizer.name(name); GaugeImpl gauge = gauges.get(name); if (gauge != null) { return gauge; } - synchronized (gauges) { - if (!gauges.containsKey(name)) { - gauges.put(name, new GaugeImpl()); - } + gauges.putIfAbsent(name, new GaugeImpl()); + return gauges.get(name); - gauge = gauges.get(name); - } - - return gauge; } @Override public Timer timer(String name) { + name = sanitizer.name(name); TimerImpl timer = timers.get(name); if (timer != null) { return timer; } - synchronized (timers) { - if (!timers.containsKey(name)) { - timers.put(name, new TimerImpl(fullyQualifiedName(name), tags, reporter)); - } - - timer = timers.get(name); - } + timers.putIfAbsent(name, new TimerImpl(fullyQualifiedName(name), tags, reporter)); + return timers.get(name); - return timer; } @Override public Histogram histogram(String name, Buckets buckets) { + name = sanitizer.name(name); if (buckets == null) { buckets = defaultBuckets; } @@ -132,24 +153,18 @@ public Histogram histogram(String name, Buckets buckets) { return histogram; } - synchronized (histograms) { - if (!histograms.containsKey(name)) { - histograms.put(name, new HistogramImpl(fullyQualifiedName(name), tags, reporter, buckets)); - } - - histogram = histograms.get(name); - } - - return histogram; + histograms.putIfAbsent(name, new HistogramImpl(fullyQualifiedName(name), tags, reporter, buckets)); + return histograms.get(name); } @Override public Scope tagged(Map tags) { - return subScopeHelper(prefix, tags); + return subScopeHelper(prefix, copyAndSanitizeMap(tags)); } @Override public Scope subScope(String name) { + name = sanitizer.name(name); return subScopeHelper(fullyQualifiedName(name), null); } @@ -180,9 +195,10 @@ public void close() { /** * Reports using the specified reporter. + * * @param reporter the reporter to report */ - void report(StatsReporter reporter) { + private void report(StatsReporter reporter) { for (Map.Entry counter : counters.entrySet()) { counter.getValue().report(fullyQualifiedName(counter.getKey()), tags, reporter); } @@ -201,40 +217,7 @@ void report(StatsReporter reporter) { reporter.flush(); } - // Serializes a map to generate a key for a prefix/map combination - // Non-generic EMPTY ImmutableMap will never contain any elements - @SuppressWarnings("unchecked") - static String keyForPrefixedStringMap(String prefix, ImmutableMap stringMap) { - if (prefix == null) { - prefix = ""; - } - - if (stringMap == null) { - stringMap = ImmutableMap.EMPTY; - } - - Set keySet = stringMap.keySet(); - String[] sortedKeys = keySet.toArray(new String[keySet.size()]); - Arrays.sort(sortedKeys); - - StringBuilder keyBuffer = new StringBuilder(prefix.length() + sortedKeys.length * 20); - keyBuffer.append(prefix); - keyBuffer.append("+"); - - for (int i = 0; i < sortedKeys.length; i++) { - keyBuffer.append(sortedKeys[i]); - keyBuffer.append("="); - keyBuffer.append(stringMap.get(sortedKeys[i])); - - if (i != sortedKeys.length - 1) { - keyBuffer.append(","); - } - } - - return keyBuffer.toString(); - } - - String fullyQualifiedName(String name) { + private String fullyQualifiedName(String name) { if (prefix == null || prefix.length() == 0) { return name; } @@ -244,8 +227,10 @@ String fullyQualifiedName(String name) { /** * Returns a {@link Snapshot} of this {@link Scope}. + * * @return a {@link Snapshot} of this {@link Scope} */ + @Override public Snapshot snapshot() { Snapshot snap = new SnapshotImpl(); @@ -315,42 +300,39 @@ public Snapshot snapshot() { return snap; } - // Helper function used to create subscopes - private Scope subScopeHelper(String prefix, Map tags) { - ImmutableMap.Builder mapBuilder = new ImmutableMap.Builder<>(); - - if (this.tags != null) { - mapBuilder.putAll(this.tags); - } + private ImmutableMap copyAndSanitizeMap(Map tags) { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); if (tags != null) { - // New tags override old tag buckets - mapBuilder.putAll(tags); + tags.forEach((key, value) -> builder.put(sanitizer.key(key), sanitizer.value(value))); } + return builder.build(); + } - ImmutableMap mergedTags = mapBuilder.build(); + // Helper function used to create subscopes + private Scope subScopeHelper(String prefix, ImmutableMap tags) { + ImmutableMap mergedTags = this.tags; + if (tags != null) { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + builder.putAll(this.tags); + // New tags override old tags + builder.putAll(tags); + mergedTags = builder.build(); + } String key = keyForPrefixedStringMap(prefix, mergedTags); - Scope subscope; - - synchronized (registry.allocationLock) { - if (!registry.subscopes.containsKey(key)) { - registry.subscopes.put( - key, - new ScopeBuilder(scheduler, registry) - .reporter(reporter) - .prefix(prefix) - .separator(separator) - .tags(mergedTags) - .defaultBuckets(defaultBuckets) - .build() - ); - } - - subscope = registry.subscopes.get(key); - } - - return subscope; + registry.subscopes.putIfAbsent( + key, + new ScopeBuilder(scheduler, registry) + .reporter(reporter) + .prefix(prefix) + .separator(separator) + .tags(mergedTags) + .defaultBuckets(defaultBuckets) + .sanitizer(sanitizer) + .build() + ); + return registry.subscopes.get(key); } // One iteration of reporting this scope and all its subscopes @@ -364,6 +346,10 @@ private void reportLoopIteration() { } } + static class Registry { + Map subscopes = new ConcurrentHashMap<>(); + } + class ReportLoop implements Runnable { private final Thread.UncaughtExceptionHandler uncaughtExceptionHandler; @@ -389,9 +375,4 @@ private void reportUncaughtException(Exception uncaughtException) { } } } - - static class Registry { - final Object allocationLock = new Object(); - Map subscopes = new ConcurrentHashMap<>(); - } } diff --git a/core/src/main/java/com/uber/m3/tally/SnapshotImpl.java b/core/src/main/java/com/uber/m3/tally/SnapshotImpl.java index 3673b35..a8a1d41 100644 --- a/core/src/main/java/com/uber/m3/tally/SnapshotImpl.java +++ b/core/src/main/java/com/uber/m3/tally/SnapshotImpl.java @@ -27,10 +27,10 @@ * Default implementation of a {@link Snapshot}. */ class SnapshotImpl implements Snapshot { - Map counters = new ConcurrentHashMap<>(); - Map gauges = new ConcurrentHashMap<>(); - Map timers = new ConcurrentHashMap<>(); - Map histograms = new ConcurrentHashMap<>(); + private Map counters = new ConcurrentHashMap<>(); + private Map gauges = new ConcurrentHashMap<>(); + private Map timers = new ConcurrentHashMap<>(); + private Map histograms = new ConcurrentHashMap<>(); @Override public Map counters() { diff --git a/core/src/main/java/com/uber/m3/tally/Stopwatch.java b/core/src/main/java/com/uber/m3/tally/Stopwatch.java index bc4672e..1cf310e 100644 --- a/core/src/main/java/com/uber/m3/tally/Stopwatch.java +++ b/core/src/main/java/com/uber/m3/tally/Stopwatch.java @@ -47,13 +47,4 @@ public Stopwatch(long startNanos, StopwatchRecorder recorder) { public void stop() { recorder.recordStopwatch(startNanos); } - - /** - * Stop the stopwatch. - * @deprecated because the wrong casing was used. Use {@link #stop()} instead. - */ - @Deprecated - public void Stop() { - stop(); - } } diff --git a/core/src/main/java/com/uber/m3/tally/TestScope.java b/core/src/main/java/com/uber/m3/tally/TestScope.java new file mode 100644 index 0000000..992fedc --- /dev/null +++ b/core/src/main/java/com/uber/m3/tally/TestScope.java @@ -0,0 +1,60 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally; + +import com.uber.m3.util.Duration; + +import java.util.Map; + +/** + * TestScope is a metrics collector that has no reporting, ensuring that + * all emitted values have a given prefix or set of tags. + */ +interface TestScope extends Scope { + + /** + * Creates a new TestScope that adds the ability to take snapshots of + * metrics emitted to it. + */ + static TestScope create() { + return (TestScope) new RootScopeBuilder() + .reporter(new NullStatsReporter()) + .reportEvery(Duration.ZERO); + } + + /** + * Creates a new TestScope with given prefix/tags that adds the ability to + * take snapshots of metrics emitted to it. + */ + static TestScope create(String prefix, Map tags) { + return (TestScope) new RootScopeBuilder() + .prefix(prefix) + .tags(tags) + .reporter(new NullStatsReporter()) + .reportEvery(Duration.ZERO); + } + + /** + * Snapshot returns a copy of all values since the last report execution + * This is an expensive operation and should only be used for testing purposes. + */ + Snapshot snapshot(); +} diff --git a/core/src/main/java/com/uber/m3/tally/sanitizers/Sanitize.java b/core/src/main/java/com/uber/m3/tally/sanitizers/Sanitize.java new file mode 100644 index 0000000..9316d12 --- /dev/null +++ b/core/src/main/java/com/uber/m3/tally/sanitizers/Sanitize.java @@ -0,0 +1,34 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally.sanitizers; + +/** + * Sanitize returns a sanitized version of the input string value. + */ +public interface Sanitize { + + /** + * Sanitize the input string value. + * @param value input string value + * @return sanitized string value + */ + String sanitize(String value); +} diff --git a/core/src/main/java/com/uber/m3/tally/sanitizers/SanitizeRange.java b/core/src/main/java/com/uber/m3/tally/sanitizers/SanitizeRange.java new file mode 100644 index 0000000..7448235 --- /dev/null +++ b/core/src/main/java/com/uber/m3/tally/sanitizers/SanitizeRange.java @@ -0,0 +1,47 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally.sanitizers; + +/** + * SanitizeRange is a range of characters (inclusive on both ends). + */ +public class SanitizeRange { + + private final char low; + private final char high; + + private SanitizeRange(char low, char high) { + this.low = low; + this.high = high; + } + + public static SanitizeRange of(char low, char high) { + return new SanitizeRange(low, high); + } + + char low() { + return low; + } + + char high() { + return high; + } +} diff --git a/core/src/main/java/com/uber/m3/tally/sanitizers/Sanitizer.java b/core/src/main/java/com/uber/m3/tally/sanitizers/Sanitizer.java new file mode 100644 index 0000000..ce640ed --- /dev/null +++ b/core/src/main/java/com/uber/m3/tally/sanitizers/Sanitizer.java @@ -0,0 +1,48 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally.sanitizers; + +/** + * SanitizerImpl sanitizes the provided input based on the function executed. + */ +public interface Sanitizer { + + /** + * Name sanitizes the provided 'name' string. + * @param name the name string + * @return the sanitized name + */ + String name(String name); + + /** + * Key sanitizes the provided 'key' string. + * @param key the key string + * @return the sanitized key + */ + String key(String key); + + /** + * Value sanitizes the provided 'value' string. + * @param value the value string + * @return the sanitized value + */ + String value(String value); +} diff --git a/core/src/main/java/com/uber/m3/tally/sanitizers/SanitizerBuilder.java b/core/src/main/java/com/uber/m3/tally/sanitizers/SanitizerBuilder.java new file mode 100644 index 0000000..d86ed14 --- /dev/null +++ b/core/src/main/java/com/uber/m3/tally/sanitizers/SanitizerBuilder.java @@ -0,0 +1,87 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally.sanitizers; + +/** + * The SanitizerBuilder returns a Sanitizer for the name, key and value. By + * default, the name, key and value sanitize functions returns all the input + * untouched. Custom name, key or value Sanitize functions or ValidCharacters + * can be provided to override their default behaviour. + */ +public class SanitizerBuilder { + + private Sanitize nameSanitizer = value -> value; + private Sanitize keySanitizer = value -> value; + private Sanitize valueSanitizer = value -> value; + + private char repChar = ValidCharacters.DEFAULT_REPLACEMENT_CHARACTER; + private ValidCharacters nameCharacters; + private ValidCharacters keyCharacters; + private ValidCharacters valueCharacters; + + public SanitizerBuilder withNameSanitizer(Sanitize nameSanitizer) { + this.nameSanitizer = nameSanitizer; + return this; + } + + public SanitizerBuilder withKeySanitizer(Sanitize keySanitizer) { + this.keySanitizer = keySanitizer; + return this; + } + + public SanitizerBuilder withValueSanitizer(Sanitize valueSanitizer) { + this.valueSanitizer = valueSanitizer; + return this; + } + + public SanitizerBuilder withReplacementCharacter(char repChar) { + this.repChar = repChar; + return this; + } + + public SanitizerBuilder withNameCharacters(ValidCharacters nameCharacters) { + this.nameCharacters = nameCharacters; + return this; + } + + public SanitizerBuilder withKeyCharacters(ValidCharacters keyCharacters) { + this.keyCharacters = keyCharacters; + return this; + } + + public SanitizerBuilder withValueCharacters(ValidCharacters valueCharacters) { + this.valueCharacters = valueCharacters; + return this; + } + + public Sanitizer build() { + if (nameCharacters != null) { + nameSanitizer = nameCharacters.sanitize(repChar); + } + if (keyCharacters != null) { + keySanitizer = keyCharacters.sanitize(repChar); + } + if (valueCharacters != null) { + valueSanitizer = valueCharacters.sanitize(repChar); + } + return new SanitizerImpl(nameSanitizer, keySanitizer, valueSanitizer); + } +} diff --git a/core/src/main/java/com/uber/m3/tally/sanitizers/SanitizerImpl.java b/core/src/main/java/com/uber/m3/tally/sanitizers/SanitizerImpl.java new file mode 100644 index 0000000..32ec77b --- /dev/null +++ b/core/src/main/java/com/uber/m3/tally/sanitizers/SanitizerImpl.java @@ -0,0 +1,64 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally.sanitizers; + +/** + * SanitizerImpl sanitizes the provided input based on the function executed. + */ +class SanitizerImpl implements Sanitizer { + + private final Sanitize nameSanitizer; + private final Sanitize keySanitizer; + private final Sanitize valueSanitizer; + + SanitizerImpl(Sanitize nameSanitizer, Sanitize keySanitizer, Sanitize valueSanitizer) { + this.nameSanitizer = nameSanitizer; + this.keySanitizer = keySanitizer; + this.valueSanitizer = valueSanitizer; + } + + /** + * Name sanitizes the provided 'name' string. + * @param name the name string + * @return the sanitized name + */ + public String name(String name) { + return this.nameSanitizer.sanitize(name); + } + + /** + * Key sanitizes the provided 'key' string. + * @param key the key string + * @return the sanitized key + */ + public String key(String key) { + return this.keySanitizer.sanitize(key); + } + + /** + * Value sanitizes the provided 'value' string. + * @param value the value string + * @return the sanitized value + */ + public String value(String value) { + return this.valueSanitizer.sanitize(value); + } +} diff --git a/core/src/main/java/com/uber/m3/tally/sanitizers/ValidCharacters.java b/core/src/main/java/com/uber/m3/tally/sanitizers/ValidCharacters.java new file mode 100644 index 0000000..4e00104 --- /dev/null +++ b/core/src/main/java/com/uber/m3/tally/sanitizers/ValidCharacters.java @@ -0,0 +1,109 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally.sanitizers; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * ValidCharacters is a collection of valid characters. + */ +public class ValidCharacters { + + /** + * DEFAULT_REPLACEMENT_CHARACTER is the default character used for replacements. + */ + public static char DEFAULT_REPLACEMENT_CHARACTER = '_'; + + /** + * ALPHANUMERIC_RANGE is the range of alphanumeric characters. + */ + public static final List ALPHANUMERIC_RANGE = + Arrays.asList( + SanitizeRange.of('a', 'z'), + SanitizeRange.of('A', 'Z'), + SanitizeRange.of('0', '9')); + + /** + * UNDERSCORE_CHARACTERS contains the underscore character. + */ + public static final List UNDERSCORE_CHARACTERS = Collections.singletonList('_'); + + /** + * UNDERSCORE_DASH_CHARACTERS contains the underscore and dash characters. + */ + public static final List UNDERSCORE_DASH_CHARACTERS = Arrays.asList('_', '-'); + + /** + * UNDERSCORE_DASH_DOT_CHARACTERS contains the underscore, dash and dot characters. + */ + public static final List UNDERSCORE_DASH_DOT_CHARACTERS = Arrays.asList('_', '-', '.'); + + private final List ranges; + private final List characters; + + private ValidCharacters(List ranges, List characters) { + this.ranges = ranges != null ? ranges : Collections.emptyList(); + this.characters = characters != null ? characters : Collections.emptyList(); + } + + public static ValidCharacters of(List ranges, List characters) { + return new ValidCharacters(ranges, characters); + } + + Sanitize sanitize(char repChar) { + return value -> { + StringBuilder buffer = null; + + for (int i = 0; i < value.length(); i++) { + char ch = value.charAt(i); + + // first check if the provided character is valid + boolean validCurr = + ranges.stream().anyMatch(range -> ch >= range.low() && ch <= range.high()) + || characters.stream().anyMatch(character -> character.equals(ch)); + + // if it's valid, we can optimize allocations by avoiding copying + if (validCurr) { + if (buffer != null) { + buffer.append(ch); + } + continue; + } + + // the character is invalid, and the buffer has not been initialized + // so we initialize the buffer and back-fill. + if (buffer == null) { + buffer = new StringBuilder(value.length()); + buffer.append(value, 0, i); + } + + // write the replacement character + buffer.append(repChar); + } + + // return input un-touched if the buffer has not been initialized + // otherwise, return the newly constructed buffer string + return buffer == null ? value : buffer.toString(); + }; + } +} diff --git a/core/src/main/java/com/uber/m3/util/ImmutableList.java b/core/src/main/java/com/uber/m3/util/ImmutableList.java index eed1e10..8a305f8 100644 --- a/core/src/main/java/com/uber/m3/util/ImmutableList.java +++ b/core/src/main/java/com/uber/m3/util/ImmutableList.java @@ -32,7 +32,7 @@ public class ImmutableList implements List { private final ArrayList collection; public ImmutableList(Collection collection) { - this.collection = new ArrayList(collection); + this.collection = new ArrayList<>(collection); } @Override diff --git a/core/src/test/java/com/uber/m3/tally/BucketPairImplTest.java b/core/src/test/java/com/uber/m3/tally/BucketPairImplTest.java index 5af2760..58202e4 100644 --- a/core/src/test/java/com/uber/m3/tally/BucketPairImplTest.java +++ b/core/src/test/java/com/uber/m3/tally/BucketPairImplTest.java @@ -23,10 +23,7 @@ import com.uber.m3.util.Duration; import org.junit.Test; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class BucketPairImplTest { @Test @@ -40,7 +37,7 @@ public void bucketPairs() { ) }; - assertArrayEquals(expectedPairs, BucketPairImpl.bucketPairs(null)); + assertArrayEquals(expectedPairs, BucketPair.create(null)); expectedPairs = new BucketPair[]{ new BucketPairImpl( @@ -71,15 +68,13 @@ public void bucketPairs() { assertArrayEquals( expectedPairs, - BucketPairImpl.bucketPairs(ValueBuckets.linear(0, 50, 3)) + BucketPair.create(ValueBuckets.linear(0, 50, 3)) ); } @Test public void testToString() { - BucketPair[] bucketPairs = BucketPairImpl.bucketPairs( - ValueBuckets.linear(-100, 200, 2) - ); + BucketPair[] bucketPairs = BucketPair.create(ValueBuckets.linear(-100, 200, 2)); // Unlike Duration buckets, we don't add a 0 in the buckets assertEquals(3, bucketPairs.length); @@ -87,21 +82,21 @@ public void testToString() { assertEquals("[-1m40s, 1m40s]", bucketPairs[1].toString()); assertEquals("[1m40s, 2562047h47m16.854775807s]", bucketPairs[2].toString()); - BucketPair[] emptyBucketPairs = BucketPairImpl.bucketPairs(null); + BucketPair[] emptyBucketPairs = BucketPair.create(null); assertEquals(1, emptyBucketPairs.length); assertEquals("[-2562047h47m16.854775808s, 2562047h47m16.854775807s]", emptyBucketPairs[0].toString()); } @Test public void equalsHashCode() { - BucketPair bucketPair = BucketPairImpl.bucketPairs(null)[0]; - BucketPair sameBucketPair = BucketPairImpl.bucketPairs(null)[0]; + BucketPair bucketPair = BucketPair.create(null)[0]; + BucketPair sameBucketPair = BucketPair.create(null)[0]; - assertTrue(bucketPair.equals(sameBucketPair)); - assertTrue(bucketPair.equals(bucketPair)); + assertEquals(bucketPair, sameBucketPair); + assertEquals(bucketPair, bucketPair); assertEquals(bucketPair.hashCode(), sameBucketPair.hashCode()); - assertFalse(bucketPair.equals(null)); - assertFalse(bucketPair.equals(9)); + assertNotEquals(null, bucketPair); + assertNotEquals(9, bucketPair); } } diff --git a/core/src/test/java/com/uber/m3/tally/CapableOfTest.java b/core/src/test/java/com/uber/m3/tally/CapableOfTest.java index d839055..41cacb3 100644 --- a/core/src/test/java/com/uber/m3/tally/CapableOfTest.java +++ b/core/src/test/java/com/uber/m3/tally/CapableOfTest.java @@ -20,15 +20,13 @@ package com.uber.m3.tally; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - import org.junit.Test; +import static org.junit.Assert.*; + public class CapableOfTest { @Test - public void capabilities() throws Exception { + public void capabilities() { Capabilities capabilities = new CapableOf(false, true); assertFalse(capabilities.reporting()); @@ -45,16 +43,16 @@ public void capabilities() throws Exception { } @Test - public void equalsHashCode() throws Exception { - assertFalse(CapableOf.NONE.equals(null)); - assertFalse(CapableOf.NONE.equals(9)); + public void equalsHashCode() { + assertNotEquals(null, CapableOf.NONE); + assertNotEquals(9, CapableOf.NONE); - assertFalse(CapableOf.NONE.equals(CapableOf.REPORTING)); - assertFalse(new CapableOf(true, false).equals(new CapableOf(false, true))); + assertNotEquals(CapableOf.NONE, CapableOf.REPORTING); + assertNotEquals(new CapableOf(true, false), new CapableOf(false, true)); - assertTrue(CapableOf.REPORTING.equals(CapableOf.REPORTING)); - assertTrue(CapableOf.REPORTING.equals(new CapableOf(true, false))); + assertEquals(CapableOf.REPORTING, CapableOf.REPORTING); + assertEquals(CapableOf.REPORTING, new CapableOf(true, false)); assertEquals(CapableOf.REPORTING.hashCode(), new CapableOf(true, false).hashCode()); - assertTrue(new CapableOf(false, false).equals(new CapableOf(false, false))); + assertEquals(new CapableOf(false, false), new CapableOf(false, false)); } } diff --git a/core/src/test/java/com/uber/m3/tally/DurationBucketsTest.java b/core/src/test/java/com/uber/m3/tally/DurationBucketsTest.java index 1973063..5992a3f 100644 --- a/core/src/test/java/com/uber/m3/tally/DurationBucketsTest.java +++ b/core/src/test/java/com/uber/m3/tally/DurationBucketsTest.java @@ -20,21 +20,16 @@ package com.uber.m3.tally; +import com.uber.m3.util.Duration; +import org.hamcrest.CoreMatchers; +import org.junit.Test; + import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; -import org.hamcrest.CoreMatchers; -import org.junit.Test; - -import com.uber.m3.util.Duration; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; public class DurationBucketsTest { @Test @@ -262,10 +257,10 @@ public void equalsHashcode() { DurationBuckets buckets = DurationBuckets.linear(Duration.ZERO, Duration.ofSeconds(10), 3); DurationBuckets sameBuckets = DurationBuckets.linear(Duration.ZERO, Duration.ofSeconds(10), 3); - assertTrue(buckets.equals(sameBuckets)); + assertEquals(buckets, sameBuckets); assertEquals(buckets.hashCode(), sameBuckets.hashCode()); - assertFalse(buckets.equals(null)); - assertFalse(buckets.equals(9)); + assertNotEquals(null, buckets); + assertNotEquals(9, buckets); } } diff --git a/core/src/test/java/com/uber/m3/tally/NoopScopeTest.java b/core/src/test/java/com/uber/m3/tally/NoopScopeTest.java new file mode 100644 index 0000000..7f561c2 --- /dev/null +++ b/core/src/test/java/com/uber/m3/tally/NoopScopeTest.java @@ -0,0 +1,37 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +public class NoopScopeTest { + + @Test + public void getInstance() { + Scope scope = NoopScope.create(); + assertNotNull(scope.capabilities()); + assertFalse(scope.capabilities().reporting()); + assertFalse(scope.capabilities().tagging()); + } +} diff --git a/core/src/test/java/com/uber/m3/tally/NullStatsReporterTest.java b/core/src/test/java/com/uber/m3/tally/NullStatsReporterTest.java new file mode 100644 index 0000000..2228097 --- /dev/null +++ b/core/src/test/java/com/uber/m3/tally/NullStatsReporterTest.java @@ -0,0 +1,37 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +public class NullStatsReporterTest { + + @Test + public void capabilities() { + NullStatsReporter reporter = new NullStatsReporter(); + assertNotNull(reporter.capabilities()); + assertFalse(reporter.capabilities().reporting()); + assertFalse(reporter.capabilities().tagging()); + } +} diff --git a/core/src/test/java/com/uber/m3/tally/ScopeImplTest.java b/core/src/test/java/com/uber/m3/tally/ScopeImplTest.java index 89de860..b0aecc9 100644 --- a/core/src/test/java/com/uber/m3/tally/ScopeImplTest.java +++ b/core/src/test/java/com/uber/m3/tally/ScopeImplTest.java @@ -20,19 +20,17 @@ package com.uber.m3.tally; +import com.uber.m3.util.Duration; +import com.uber.m3.util.ImmutableMap; +import org.junit.Test; + import java.lang.Thread.UncaughtExceptionHandler; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Test; - -import com.uber.m3.util.Duration; -import com.uber.m3.util.ImmutableMap; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; public class ScopeImplTest { private static final double EPSILON = 1e-10; @@ -54,21 +52,21 @@ public void metricCreation() { Counter sameCounter = scope.counter("new-counter"); // Should be the same Counter object and not a new instance - assertTrue(counter == sameCounter); + assertEquals(counter, sameCounter); Gauge gauge = scope.gauge("new-gauge"); assertNotNull(gauge); Gauge sameGauge = scope.gauge("new-gauge"); // Should be the same Gauge object and not a new instance - assertTrue(gauge == sameGauge); + assertEquals(gauge, sameGauge); Timer timer = scope.timer("new-timer"); assertNotNull(timer); Timer sameTimer = scope.timer("new-timer"); // Should be the same Timer object and not a new instance - assertTrue(timer == sameTimer); + assertEquals(timer, sameTimer); Histogram histogram = scope.histogram( "new-histogram", @@ -78,7 +76,7 @@ public void metricCreation() { Histogram sameHistogram = scope.histogram("new-histogram", null); // Should be the same Histogram object and not a new instance - assertTrue(histogram == sameHistogram); + assertEquals(histogram, sameHistogram); } @Test @@ -151,9 +149,9 @@ public void subscopes() { ImmutableMap additionalTags = new ImmutableMap.Builder(2) - .put("new_key", "new_val") - .put("baz", "quz") - .build(); + .put("new_key", "new_val") + .put("baz", "quz") + .build(); Scope taggedSubscope = rootScope.tagged(additionalTags); Timer taggedTimer = taggedSubscope.timer("tagged_timer"); taggedTimer.record(Duration.ofSeconds(6)); @@ -180,9 +178,9 @@ public void subscopes() { assertEquals("tagged_timer", timer.getName()); ImmutableMap expectedTags = new ImmutableMap.Builder(4) - .putAll(tags) - .putAll(additionalTags) - .build(); + .putAll(tags) + .putAll(additionalTags) + .build(); assertEquals(expectedTags, timer.getTags()); } @@ -217,33 +215,43 @@ public void snapshot() { System.err.println("Interrupted while sleeping! Let's continue anyway..."); } - Snapshot snapshot = ((ScopeImpl) rootScope).snapshot(); + Snapshot snapshot = ((TestScope) rootScope).snapshot(); Map counters = snapshot.counters(); assertEquals(1, counters.size()); assertEquals("snapshot-counter", counters.get("snapshot-counter+").name()); - assertEquals(null, counters.get("snapshot-counter+").tags()); + assertNotNull(counters.get("snapshot-counter+").tags()); + assertEquals(0, counters.get("snapshot-counter+").tags().size()); Map gauges = snapshot.gauges(); assertEquals(3, gauges.size()); assertEquals("snapshot-gauge", gauges.get("snapshot-gauge+").name()); - assertEquals(null, gauges.get("snapshot-gauge+").tags()); + assertNotNull(gauges.get("snapshot-gauge+").tags()); + assertEquals(0, gauges.get("snapshot-gauge+").tags().size()); assertEquals(120, gauges.get("snapshot-gauge+").value(), EPSILON); assertEquals("snapshot-gauge2", gauges.get("snapshot-gauge2+").name()); - assertEquals(null, gauges.get("snapshot-gauge2+").tags()); + assertNotNull(gauges.get("snapshot-gauge2+").tags()); + assertEquals(0, gauges.get("snapshot-gauge2+").tags().size()); assertEquals(220, gauges.get("snapshot-gauge2+").value(), EPSILON); assertEquals("snapshot-gauge3", gauges.get("snapshot-gauge3+").name()); - assertEquals(null, gauges.get("snapshot-gauge3+").tags()); + assertNotNull(gauges.get("snapshot-gauge3+").tags()); + assertEquals(0, gauges.get("snapshot-gauge3+").tags().size()); assertEquals(320, gauges.get("snapshot-gauge3+").value(), EPSILON); Map timers = snapshot.timers(); assertEquals(1, timers.size()); assertEquals("snapshot-timer", timers.get("snapshot-timer+").name()); - assertEquals(null, timers.get("snapshot-timer+").tags()); + assertNotNull(timers.get("snapshot-timer+").tags()); + assertEquals(0, timers.get("snapshot-timer+").tags().size()); + } + + @Test + public void zeroReportInterval() { + new RootScopeBuilder().reportEvery(Duration.ZERO); } @Test(expected = IllegalArgumentException.class) - public void nonPositiveReportInterval() { + public void negativeReportInterval() { new RootScopeBuilder().reportEvery(Duration.ofSeconds(-10)); } @@ -251,19 +259,12 @@ public void nonPositiveReportInterval() { public void exceptionInReportLoop() throws ScopeCloseException, InterruptedException { final AtomicInteger uncaghtExceptionReported = new AtomicInteger(); ThrowingStatsReporter reporter = new ThrowingStatsReporter(); - final UncaughtExceptionHandler uncaughtExceptionHandler = new UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread t, Throwable e) { - uncaghtExceptionReported.incrementAndGet(); - } - }; + final UncaughtExceptionHandler uncaughtExceptionHandler = (t, e) -> uncaghtExceptionReported.incrementAndGet(); - Scope scope = new RootScopeBuilder() + try (Scope scope = new RootScopeBuilder() .reporter(reporter) .reportEvery(Duration.ofMillis(REPORT_INTERVAL_MILLIS), - uncaughtExceptionHandler); - - try { + uncaughtExceptionHandler)) { scope.counter("hi").inc(1); Thread.sleep(SLEEP_MILLIS); @@ -276,15 +277,13 @@ public void uncaughtException(Thread t, Throwable e) { assertEquals(2, uncaghtExceptionReported.get()); assertEquals(2, reporter.getNumberOfReportedMetrics()); - } finally { - scope.close(); } } private static class ThrowingStatsReporter implements StatsReporter { private final AtomicInteger reported = new AtomicInteger(); - public int getNumberOfReportedMetrics() { + int getNumberOfReportedMetrics() { return reported.get(); } @@ -307,17 +306,21 @@ public void reportTimer(String name, Map tags, Duration interval } @Override - public void reportHistogramValueSamples(String name, Map tags, - Buckets buckets, double bucketLowerBound, - double bucketUpperBound, long samples) { + public void reportHistogramValueSamples( + String name, Map tags, + Buckets buckets, double bucketLowerBound, + double bucketUpperBound, long samples + ) { reported.incrementAndGet(); throw new RuntimeException(); } @Override - public void reportHistogramDurationSamples(String name, Map tags, - Buckets buckets, Duration bucketLowerBound, - Duration bucketUpperBound, long samples) { + public void reportHistogramDurationSamples( + String name, Map tags, + Buckets buckets, Duration bucketLowerBound, + Duration bucketUpperBound, long samples + ) { reported.incrementAndGet(); throw new RuntimeException(); } diff --git a/core/src/test/java/com/uber/m3/tally/TestScopeTest.java b/core/src/test/java/com/uber/m3/tally/TestScopeTest.java new file mode 100644 index 0000000..80f7cd2 --- /dev/null +++ b/core/src/test/java/com/uber/m3/tally/TestScopeTest.java @@ -0,0 +1,65 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally; + +import com.uber.m3.util.ImmutableMap; +import org.junit.Test; + +import java.util.Map; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + +public class TestScopeTest { + + @Test + public void getInstance() { + TestScope testScope = TestScope.create(); + assertNotNull(testScope); + assertThat(testScope, instanceOf(Scope.class)); + assertThat(testScope, instanceOf(ScopeImpl.class)); + + assertNotNull(testScope.capabilities()); + assertFalse(testScope.capabilities().reporting()); + assertFalse(testScope.capabilities().tagging()); + } + + @Test + public void prefixTags() { + Map tags = ImmutableMap.of("key", "value"); + TestScope testScope = TestScope.create("prefix", tags); + testScope.counter("counter").inc(1); + + Snapshot snapshot = testScope.snapshot(); + assertNotNull(snapshot); + + Map counters = snapshot.counters(); + assertNotNull(counters); + assertEquals(1, counters.size()); + assertNotNull(counters.get("prefix.counter+key=value")); + assertEquals("prefix.counter", counters.get("prefix.counter+key=value").name()); + assertEquals(tags, counters.get("prefix.counter+key=value").tags()); + assertEquals(1, counters.get("prefix.counter+key=value").value()); + } +} diff --git a/core/src/test/java/com/uber/m3/tally/TestStatsReporter.java b/core/src/test/java/com/uber/m3/tally/TestStatsReporter.java index 7252987..8a9e183 100644 --- a/core/src/test/java/com/uber/m3/tally/TestStatsReporter.java +++ b/core/src/test/java/com/uber/m3/tally/TestStatsReporter.java @@ -27,7 +27,8 @@ import java.util.Queue; import java.util.concurrent.LinkedBlockingDeque; -public class TestStatsReporter implements StatsReporter { +class TestStatsReporter implements StatsReporter { + private Queue> counters = new LinkedBlockingDeque<>(); private Queue> gauges = new LinkedBlockingDeque<>(); private Queue> timers = new LinkedBlockingDeque<>(); @@ -40,11 +41,11 @@ public void reportCounter(String name, Map tags, long value) { counters.add(new MetricStruct<>(name, tags, value)); } - public MetricStruct nextCounter() { + MetricStruct nextCounter() { return counters.remove(); } - public long nextCounterVal() { + long nextCounterVal() { return counters.remove().getValue(); } @@ -53,11 +54,11 @@ public void reportGauge(String name, Map tags, double value) { gauges.add(new MetricStruct<>(name, tags, value)); } - public MetricStruct nextGauge() { + MetricStruct nextGauge() { return gauges.remove(); } - public double nextGaugeVal() { + double nextGaugeVal() { return gauges.remove().getValue(); } @@ -66,11 +67,11 @@ public void reportTimer(String name, Map tags, Duration interval timers.add(new MetricStruct<>(name, tags, interval)); } - public MetricStruct nextTimer() { + MetricStruct nextTimer() { return timers.remove(); } - public Duration nextTimerVal() { + Duration nextTimerVal() { return timers.remove().getValue(); } @@ -114,15 +115,15 @@ public void close() { // No-op } - public Map getDurationSamples() { + Map getDurationSamples() { return durationSamples; } - public Map getValueSamples() { + Map getValueSamples() { return valueSamples; } - public Buckets getBuckets() { + Buckets getBuckets() { return buckets; } diff --git a/core/src/test/java/com/uber/m3/tally/ValueBucketsTest.java b/core/src/test/java/com/uber/m3/tally/ValueBucketsTest.java index 27c9f7b..472b9f1 100644 --- a/core/src/test/java/com/uber/m3/tally/ValueBucketsTest.java +++ b/core/src/test/java/com/uber/m3/tally/ValueBucketsTest.java @@ -20,16 +20,11 @@ package com.uber.m3.tally; +import com.uber.m3.util.Duration; import org.hamcrest.CoreMatchers; import org.junit.Test; -import com.uber.m3.util.Duration; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; public class ValueBucketsTest { @Test @@ -154,10 +149,10 @@ public void equalsHashcode() { ValueBuckets buckets = ValueBuckets.linear(0, 10, 3); ValueBuckets sameBuckets = ValueBuckets.linear(0, 10, 3); - assertTrue(buckets.equals(sameBuckets)); + assertEquals(buckets, sameBuckets); assertEquals(buckets.hashCode(), sameBuckets.hashCode()); - assertFalse(buckets.equals(null)); - assertFalse(buckets.equals(9)); + assertNotEquals(null, buckets); + assertNotEquals(9, buckets); } } diff --git a/core/src/test/java/com/uber/m3/tally/sanitizers/SanitizeRangeTest.java b/core/src/test/java/com/uber/m3/tally/sanitizers/SanitizeRangeTest.java new file mode 100644 index 0000000..3745edc --- /dev/null +++ b/core/src/test/java/com/uber/m3/tally/sanitizers/SanitizeRangeTest.java @@ -0,0 +1,40 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally.sanitizers; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class SanitizeRangeTest { + + private static final char LOW = 'a'; + private static final char HIGH = 'z'; + + @Test + public void sanitizeRange() { + SanitizeRange range = SanitizeRange.of(LOW, HIGH); + assertNotNull(range); + assertEquals(LOW, range.low()); + assertEquals(HIGH, range.high()); + } +} diff --git a/core/src/test/java/com/uber/m3/tally/sanitizers/SanitizerTest.java b/core/src/test/java/com/uber/m3/tally/sanitizers/SanitizerTest.java new file mode 100644 index 0000000..ed7825e --- /dev/null +++ b/core/src/test/java/com/uber/m3/tally/sanitizers/SanitizerTest.java @@ -0,0 +1,108 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally.sanitizers; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SanitizerTest { + + private static final String NAME = "!@#$%^&*()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-."; + private static final String KEY = "!@#$%^&*()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-."; + private static final String VALUE = "!@#$%^&*()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-."; + private static final String SANITIZED_NAME_1 = "sanitized-name"; + private static final String SANITIZED_KEY_1 = "sanitized-key"; + private static final String SANITIZED_VALUE_1 = "sanitized-value"; + private static final String SANITIZED_NAME_2 = "__________abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890___"; + private static final String SANITIZED_KEY_2 = "__________abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-_"; + private static final String SANITIZED_VALUE_2 = "__________abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-."; + private static final String SANITIZED_NAME_3 = "@@@@@@@@@@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_@@"; + private static final String SANITIZED_KEY_3 = "@@@@@@@@@@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-@"; + private static final String SANITIZED_VALUE_3 = "@@@@@@@@@@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-."; + private static final char REPLACEMENT_CHAR = '@'; + + @Test + public void noopSanitizer() { + Sanitizer sanitizer = new SanitizerBuilder().build(); + assertEquals(NAME, sanitizer.name(NAME)); + assertEquals(KEY, sanitizer.key(KEY)); + assertEquals(VALUE, sanitizer.value(VALUE)); + } + + @Test + public void withSanitizers() { + Sanitizer sanitizer = + new SanitizerBuilder() + .withNameSanitizer(value -> SANITIZED_NAME_1) + .withKeySanitizer(value -> SANITIZED_KEY_1) + .withValueSanitizer(value -> SANITIZED_VALUE_1) + .build(); + assertEquals(SANITIZED_NAME_1, sanitizer.name(NAME)); + assertEquals(SANITIZED_KEY_1, sanitizer.key(KEY)); + assertEquals(SANITIZED_VALUE_1, sanitizer.value(VALUE)); + } + + @Test + public void withValidCharactersAndDefaultRepChar() { + Sanitizer sanitizer = + new SanitizerBuilder() + .withNameCharacters( + ValidCharacters.of( + ValidCharacters.ALPHANUMERIC_RANGE, + ValidCharacters.UNDERSCORE_CHARACTERS)) + .withKeyCharacters( + ValidCharacters.of( + ValidCharacters.ALPHANUMERIC_RANGE, + ValidCharacters.UNDERSCORE_DASH_CHARACTERS)) + .withValueCharacters( + ValidCharacters.of( + ValidCharacters.ALPHANUMERIC_RANGE, + ValidCharacters.UNDERSCORE_DASH_DOT_CHARACTERS)) + .build(); + assertEquals(SANITIZED_NAME_2, sanitizer.name(NAME)); + assertEquals(SANITIZED_KEY_2, sanitizer.key(KEY)); + assertEquals(SANITIZED_VALUE_2, sanitizer.value(VALUE)); + } + + @Test + public void withValidCharactersAndRepChar() { + Sanitizer sanitizer = + new SanitizerBuilder() + .withReplacementCharacter(REPLACEMENT_CHAR) + .withNameCharacters( + ValidCharacters.of( + ValidCharacters.ALPHANUMERIC_RANGE, + ValidCharacters.UNDERSCORE_CHARACTERS)) + .withKeyCharacters( + ValidCharacters.of( + ValidCharacters.ALPHANUMERIC_RANGE, + ValidCharacters.UNDERSCORE_DASH_CHARACTERS)) + .withValueCharacters( + ValidCharacters.of( + ValidCharacters.ALPHANUMERIC_RANGE, + ValidCharacters.UNDERSCORE_DASH_DOT_CHARACTERS)) + .build(); + assertEquals(SANITIZED_NAME_3, sanitizer.name(NAME)); + assertEquals(SANITIZED_KEY_3, sanitizer.key(KEY)); + assertEquals(SANITIZED_VALUE_3, sanitizer.value(VALUE)); + } +} diff --git a/core/src/test/java/com/uber/m3/util/DurationTest.java b/core/src/test/java/com/uber/m3/util/DurationTest.java index 5cacee0..4dc35b3 100644 --- a/core/src/test/java/com/uber/m3/util/DurationTest.java +++ b/core/src/test/java/com/uber/m3/util/DurationTest.java @@ -22,9 +22,7 @@ import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class DurationTest { private static final double EPSILON = 1e-10; @@ -102,15 +100,15 @@ public void compareTo() { @Test public void equals() { - assertTrue(Duration.ZERO.equals(Duration.ZERO)); - assertTrue(Duration.ZERO.equals(Duration.ofNanos(0))); - assertTrue(Duration.ofMillis(6000).equals(Duration.ofSeconds(6))); - assertTrue(Duration.ofHours(1).equals(Duration.ofNanos(3_600_000_000_000L))); + assertEquals(Duration.ZERO, Duration.ZERO); + assertEquals(Duration.ZERO, Duration.ofNanos(0)); + assertEquals(Duration.ofMillis(6000), Duration.ofSeconds(6)); + assertEquals(Duration.ofHours(1), Duration.ofNanos(3_600_000_000_000L)); assertEquals(Duration.ofHours(1).hashCode(), Duration.ofNanos(3_600_000_000_000L).hashCode()); - assertFalse(Duration.ofMillis(6001).equals(Duration.ofSeconds(6))); - assertFalse(Duration.ZERO.equals(null)); - assertFalse(Duration.ZERO.equals(1)); + assertNotEquals(Duration.ofMillis(6001), Duration.ofSeconds(6)); + assertNotEquals(null, Duration.ZERO); + assertNotEquals(1, Duration.ZERO); } @Test diff --git a/core/src/test/java/com/uber/m3/util/ImmutableListTest.java b/core/src/test/java/com/uber/m3/util/ImmutableListTest.java index f5d82be..440ea27 100644 --- a/core/src/test/java/com/uber/m3/util/ImmutableListTest.java +++ b/core/src/test/java/com/uber/m3/util/ImmutableListTest.java @@ -48,7 +48,7 @@ public void setUp() { public void size() { assertEquals(2, list.size()); - list = new ImmutableList<>(new ArrayList(0)); + list = new ImmutableList<>(new ArrayList<>(0)); assertEquals(0, list.size()); } @@ -57,7 +57,7 @@ public void size() { public void isEmpty() { assertFalse(list.isEmpty()); - list = new ImmutableList<>(new ArrayList(0)); + list = new ImmutableList<>(new ArrayList<>(0)); assertTrue(list.isEmpty()); } @@ -202,13 +202,13 @@ public void equals() { helperList.add("zz"); ImmutableList differentList = new ImmutableList<>(helperList); - assertTrue(list.equals(sameList)); + assertEquals(list, sameList); assertEquals(list.hashCode(), sameList.hashCode()); - assertFalse(list.equals(differentList)); + assertNotEquals(list, differentList); assertNotEquals(list.hashCode(), differentList.hashCode()); - assertFalse(list.equals(null)); - assertTrue(list.equals(list)); - assertFalse(list.equals(1)); + assertNotEquals(null, list); + assertEquals(list, list); + assertNotEquals(1, list); } } diff --git a/core/src/test/java/com/uber/m3/util/ImmutableMapTest.java b/core/src/test/java/com/uber/m3/util/ImmutableMapTest.java index 2e0a6c1..0027f3c 100644 --- a/core/src/test/java/com/uber/m3/util/ImmutableMapTest.java +++ b/core/src/test/java/com/uber/m3/util/ImmutableMapTest.java @@ -25,10 +25,7 @@ import java.util.HashMap; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class ImmutableMapTest { private HashMap helperMap; @@ -66,18 +63,18 @@ public void of() { public void size() { assertEquals(3, map.size()); - map = new ImmutableMap<>(new HashMap(0, 1)); + map = new ImmutableMap<>(new HashMap<>(0, 1)); assertEquals(0, map.size()); } @Test public void isEmpty() { - assertEquals(false, map.isEmpty()); + assertFalse(map.isEmpty()); - map = new ImmutableMap<>(new HashMap(0, 1)); + map = new ImmutableMap<>(new HashMap<>(0, 1)); - assertEquals(true, map.isEmpty()); + assertTrue(map.isEmpty()); } @Test @@ -102,7 +99,7 @@ public void containsValue() { public void get() { assertEquals("val1", map.get("key1")); - assertEquals(null, map.get("key9")); + assertNull(map.get("key9")); } @Test(expected = UnsupportedOperationException.class) @@ -145,27 +142,27 @@ public void builderPutAll() { ImmutableMap sameMap = new ImmutableMap.Builder() .putAll(helperMap).build(); - assertTrue(map.equals(sameMap)); + assertEquals(map, sameMap); } @Test public void equals() { - assertFalse(map.equals(null)); - assertFalse(map.equals(1)); - assertTrue(map.equals(map)); + assertNotEquals(null, map); + assertNotEquals(1, map); + assertEquals(map, map); ImmutableMap sameMap = new ImmutableMap<>(helperMap); - assertTrue(map.equals(sameMap)); + assertEquals(map, sameMap); assertEquals(map.hashCode(), sameMap.hashCode()); helperMap.put("key7", "val7"); ImmutableMap differentMap = new ImmutableMap<>(helperMap); - assertFalse(map.equals(differentMap)); + assertNotEquals(map, differentMap); assertNotEquals(map.hashCode(), differentMap.hashCode()); - assertTrue(ImmutableMap.EMPTY.equals(new ImmutableMap(new HashMap()))); + assertEquals(ImmutableMap.EMPTY, new ImmutableMap(new HashMap())); } @Test diff --git a/core/src/test/java/com/uber/m3/util/ImmutableSetTest.java b/core/src/test/java/com/uber/m3/util/ImmutableSetTest.java index 3689019..890b757 100644 --- a/core/src/test/java/com/uber/m3/util/ImmutableSetTest.java +++ b/core/src/test/java/com/uber/m3/util/ImmutableSetTest.java @@ -50,7 +50,7 @@ public void setUp() { public void size() { assertEquals(4, set.size()); - set = new ImmutableSet<>(new HashSet(0)); + set = new ImmutableSet<>(new HashSet<>(0)); assertEquals(0, set.size()); } @@ -59,7 +59,7 @@ public void size() { public void isEmpty() { assertFalse(set.isEmpty()); - set = new ImmutableSet<>(new HashSet(0)); + set = new ImmutableSet<>(new HashSet<>(0)); assertTrue(set.isEmpty()); } @@ -119,7 +119,7 @@ public void containsAll() { @Test(expected = UnsupportedOperationException.class) public void addAll() { - set.addAll(new HashSet()); + set.addAll(new HashSet<>()); } @Test(expected = UnsupportedOperationException.class) @@ -141,18 +141,18 @@ public void clear() { public void equals() { ImmutableSet equalSet = new ImmutableSet<>(helperSet); - assertTrue(set.equals(equalSet)); + assertEquals(set, equalSet); assertEquals(set.hashCode(), equalSet.hashCode()); helperSet.add("zz"); ImmutableSet differentSet = new ImmutableSet<>(helperSet); - assertFalse(set.equals(differentSet)); + assertNotEquals(set, differentSet); assertNotEquals(set.hashCode(), differentSet.hashCode()); - assertFalse(set.equals(null)); - assertTrue(set.equals(set)); - assertFalse(set.equals(2)); + assertNotEquals(null, set); + assertEquals(set, set); + assertNotEquals(2, set); } @Test diff --git a/m3/src/main/java/com/uber/m3/tally/m3/M3Reporter.java b/m3/src/main/java/com/uber/m3/tally/m3/M3Reporter.java index 923d0cd..a28f8fe 100644 --- a/m3/src/main/java/com/uber/m3/tally/m3/M3Reporter.java +++ b/m3/src/main/java/com/uber/m3/tally/m3/M3Reporter.java @@ -21,7 +21,6 @@ package com.uber.m3.tally.m3; import com.uber.m3.tally.BucketPair; -import com.uber.m3.tally.BucketPairImpl; import com.uber.m3.tally.Buckets; import com.uber.m3.tally.Capabilities; import com.uber.m3.tally.CapableOf; @@ -44,6 +43,9 @@ import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolFactory; import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.net.SocketAddress; @@ -63,10 +65,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.apache.thrift.transport.TTransportException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * An M3 implementation of a {@link StatsReporter}. */ @@ -160,7 +158,7 @@ private M3Reporter(Builder builder) { private int calculateFreeBytes(int maxPacketSizeBytes, Set commonTags) { MetricBatch metricBatch = new MetricBatch(); metricBatch.setCommonTags(commonTags); - metricBatch.setMetrics(new ArrayList()); + metricBatch.setMetrics(new ArrayList<>()); int size; @@ -373,7 +371,7 @@ public void reportHistogramValueSamples( String bucketIdFmt = String.format("%%0%sd", bucketIdLen); - BucketPair[] bucketPairs = BucketPairImpl.bucketPairs(buckets); + BucketPair[] bucketPairs = BucketPair.create(buckets); if (tags == null) { // We know that the HashMap will only contain two items at this point, @@ -421,7 +419,7 @@ public void reportHistogramDurationSamples( String bucketIdFmt = String.format("%%0%sd", bucketIdLen); - BucketPair[] bucketPairs = BucketPairImpl.bucketPairs(buckets); + BucketPair[] bucketPairs = BucketPair.create(buckets); if (tags == null) { // We know that the HashMap will only contain two items at this point, @@ -480,7 +478,7 @@ private void queueSizedMetric(SizedMetric sizedMetric) { try { metricQueue.put(sizedMetric); } catch (InterruptedException e) { - LOG.warn(String.format("Interrupted queueing metric: {}", sizedMetric.getMetric().getName())); + LOG.warn("Interrupted queueing metric: {}", sizedMetric.getMetric().getName()); } } diff --git a/m3/src/main/java/com/uber/m3/tally/m3/M3Sanitizer.java b/m3/src/main/java/com/uber/m3/tally/m3/M3Sanitizer.java new file mode 100644 index 0000000..fbba346 --- /dev/null +++ b/m3/src/main/java/com/uber/m3/tally/m3/M3Sanitizer.java @@ -0,0 +1,50 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally.m3; + +import com.uber.m3.tally.sanitizers.Sanitizer; +import com.uber.m3.tally.sanitizers.SanitizerBuilder; +import com.uber.m3.tally.sanitizers.ValidCharacters; + +public class M3Sanitizer { + + /** + * Creates the default M3 sanitizer. + * @return default M3 sanitizer + */ + public static Sanitizer create() { + return new SanitizerBuilder() + .withReplacementCharacter(ValidCharacters.DEFAULT_REPLACEMENT_CHARACTER) + .withNameCharacters( + ValidCharacters.of( + ValidCharacters.ALPHANUMERIC_RANGE, + ValidCharacters.UNDERSCORE_DASH_DOT_CHARACTERS)) + .withKeyCharacters( + ValidCharacters.of( + ValidCharacters.ALPHANUMERIC_RANGE, + ValidCharacters.UNDERSCORE_DASH_CHARACTERS)) + .withValueCharacters( + ValidCharacters.of( + ValidCharacters.ALPHANUMERIC_RANGE, + ValidCharacters.UNDERSCORE_DASH_DOT_CHARACTERS)) + .build(); + } +} diff --git a/m3/src/test/java/com/uber/m3/tally/m3/M3ReporterTest.java b/m3/src/test/java/com/uber/m3/tally/m3/M3ReporterTest.java index 7746e19..9cb2e01 100644 --- a/m3/src/test/java/com/uber/m3/tally/m3/M3ReporterTest.java +++ b/m3/src/test/java/com/uber/m3/tally/m3/M3ReporterTest.java @@ -23,7 +23,6 @@ import com.uber.m3.tally.Buckets; import com.uber.m3.tally.CapableOf; import com.uber.m3.tally.DurationBuckets; - import com.uber.m3.tally.ValueBuckets; import com.uber.m3.thrift.gen.CountValue; import com.uber.m3.thrift.gen.GaugeValue; @@ -32,7 +31,6 @@ import com.uber.m3.thrift.gen.MetricTag; import com.uber.m3.thrift.gen.MetricValue; import com.uber.m3.thrift.gen.TimerValue; - import com.uber.m3.util.Duration; import com.uber.m3.util.ImmutableMap; import org.junit.BeforeClass; @@ -47,15 +45,10 @@ import java.util.Map; import java.util.concurrent.Phaser; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class M3ReporterTest { - private static double EPSILON = 1e-9; - - private static SocketAddress socketAddress; + private static final double EPSILON = 1e-9; private static final int MAX_QUEUE_SIZE = 1000; private static final int MAX_PACKET_SIZE_BYTES = 1440; @@ -66,6 +59,8 @@ public class M3ReporterTest { "host", "test-host" ); + private static SocketAddress socketAddress; + @BeforeClass public static void setup() { try { @@ -82,12 +77,7 @@ public void reporter() { final MockM3Server server = new MockM3Server(phaser, true, socketAddress); M3Reporter reporter = null; - Thread serverThread = new Thread(new Runnable() { - @Override - public void run() { - server.serve(); - } - }); + Thread serverThread = new Thread(server::serve); try { serverThread.start(); @@ -244,12 +234,7 @@ public void reporterFinalFlush() { final MockM3Server server = new MockM3Server(phaser, true, socketAddress); - Thread serverThread = new Thread(new Runnable() { - @Override - public void run() { - server.serve(); - } - }); + Thread serverThread = new Thread(server::serve); try { serverThread.start(); @@ -280,12 +265,7 @@ public void run() { public void reporterAfterCloseNoThrow() { final MockM3Server server = new MockM3Server(new Phaser(), true, socketAddress); - Thread serverThread = new Thread(new Runnable() { - @Override - public void run() { - server.serve(); - } - }); + Thread serverThread = new Thread(server::serve); try { serverThread.start(); @@ -313,12 +293,7 @@ public void reporterHistogramDurations() { final MockM3Server server = new MockM3Server(phaser, true, socketAddress); - Thread serverThread = new Thread(new Runnable() { - @Override - public void run() { - server.serve(); - } - }); + Thread serverThread = new Thread(server::serve); try { serverThread.start(); @@ -422,12 +397,7 @@ public void reporterHistogramValues() { final MockM3Server server = new MockM3Server(phaser, true, socketAddress); - Thread serverThread = new Thread(new Runnable() { - @Override - public void run() { - server.serve(); - } - }); + Thread serverThread = new Thread(server::serve); try { serverThread.start(); diff --git a/m3/src/test/java/com/uber/m3/tally/m3/M3SanitizerTest.java b/m3/src/test/java/com/uber/m3/tally/m3/M3SanitizerTest.java new file mode 100644 index 0000000..abfd933 --- /dev/null +++ b/m3/src/test/java/com/uber/m3/tally/m3/M3SanitizerTest.java @@ -0,0 +1,45 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.uber.m3.tally.m3; + +import com.uber.m3.tally.sanitizers.Sanitizer; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class M3SanitizerTest { + + private static final String NAME = "!@#$%^&*()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-."; + private static final String KEY = "!@#$%^&*()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-."; + private static final String VALUE = "!@#$%^&*()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-."; + private static final String SANITIZED_NAME = "__________abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-."; + private static final String SANITIZED_KEY = "__________abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-_"; + private static final String SANITIZED_VALUE = "__________abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-."; + + @Test + public void m3Sanitizer() { + Sanitizer sanitizer = M3Sanitizer.create(); + assertNotNull(sanitizer); + assertEquals(SANITIZED_NAME, sanitizer.name(NAME)); + assertEquals(SANITIZED_KEY, sanitizer.key(KEY)); + assertEquals(SANITIZED_VALUE, sanitizer.value(VALUE)); + } +} diff --git a/m3/src/test/java/com/uber/m3/tally/m3/MockM3Service.java b/m3/src/test/java/com/uber/m3/tally/m3/MockM3Service.java index 0ead031..5207b5d 100644 --- a/m3/src/test/java/com/uber/m3/tally/m3/MockM3Service.java +++ b/m3/src/test/java/com/uber/m3/tally/m3/MockM3Service.java @@ -32,11 +32,11 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; public class MockM3Service implements M3.Iface { - ReadWriteLock lock = new ReentrantReadWriteLock(); - List batches = new ArrayList<>(); - List metrics = new ArrayList<>(); - Phaser phaser; - boolean countBatches; + private ReadWriteLock lock = new ReentrantReadWriteLock(); + private List batches = new ArrayList<>(); + private List metrics = new ArrayList<>(); + private Phaser phaser; + private boolean countBatches; public MockM3Service(Phaser phaser, boolean countBatches) { this.phaser = phaser; diff --git a/m3/src/test/java/com/uber/m3/tally/m3/SizedMetricTest.java b/m3/src/test/java/com/uber/m3/tally/m3/SizedMetricTest.java index 2a7287a..31a76a2 100644 --- a/m3/src/test/java/com/uber/m3/tally/m3/SizedMetricTest.java +++ b/m3/src/test/java/com/uber/m3/tally/m3/SizedMetricTest.java @@ -24,6 +24,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; public class SizedMetricTest { @Test @@ -40,7 +41,7 @@ public void simpleSizedMetric() { public void nullSizedMetric() { SizedMetric sizedMetric = new SizedMetric(null, 9); - assertEquals(sizedMetric.getMetric(), null); + assertNull(sizedMetric.getMetric()); assertEquals(sizedMetric.getSize(), 9); } diff --git a/m3/src/test/java/com/uber/m3/tally/m3/TCalcTransportTest.java b/m3/src/test/java/com/uber/m3/tally/m3/TCalcTransportTest.java index f656d0c..beaa18d 100644 --- a/m3/src/test/java/com/uber/m3/tally/m3/TCalcTransportTest.java +++ b/m3/src/test/java/com/uber/m3/tally/m3/TCalcTransportTest.java @@ -25,11 +25,10 @@ import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class TCalcTransportTest { - TCalcTransport transport; + private TCalcTransport transport; @Before public void setUp() { @@ -50,7 +49,7 @@ public void open() { transport.open(); } catch (TTransportException e) { // Should not reach here - assertTrue(false); + fail(); } } @@ -66,7 +65,7 @@ public void read() { assertEquals(0, transport.read(null, 0, 0)); } catch (TTransportException e) { // Should not reach here - assertTrue(false); + fail(); } } @@ -99,7 +98,7 @@ public void writeGetSizeReset() { assertEquals(0, transport.getSize()); } catch (TTransportException e) { // Should not reach here - assertTrue(false); + fail(); } } } diff --git a/statsd/src/main/java/com/uber/m3/tally/statsd/StatsdAssertingUdpServer.java b/statsd/src/main/java/com/uber/m3/tally/statsd/StatsdAssertingUdpServer.java index 11e8df1..9ea0d36 100644 --- a/statsd/src/main/java/com/uber/m3/tally/statsd/StatsdAssertingUdpServer.java +++ b/statsd/src/main/java/com/uber/m3/tally/statsd/StatsdAssertingUdpServer.java @@ -29,10 +29,11 @@ import java.util.Set; public class StatsdAssertingUdpServer implements Runnable { - private final int TIMEOUT_MILLIS = 1000; - private final int RECEIVE_MAX_SIZE = 1024; + private static final int TIMEOUT_MILLIS = 1000; + private static final int RECEIVE_MAX_SIZE = 1024; + private final SocketAddress socketAddress; - private Set expectedStrs; + private final Set expectedStrs; StatsdAssertingUdpServer(String hostname, int port, Set expectedStrs) { this.expectedStrs = expectedStrs; diff --git a/statsd/src/test/java/com/uber/m3/tally/statsd/StatsdReporterTest.java b/statsd/src/test/java/com/uber/m3/tally/statsd/StatsdReporterTest.java index eddf17a..7008e28 100644 --- a/statsd/src/test/java/com/uber/m3/tally/statsd/StatsdReporterTest.java +++ b/statsd/src/test/java/com/uber/m3/tally/statsd/StatsdReporterTest.java @@ -34,9 +34,8 @@ import static org.junit.Assert.assertEquals; public class StatsdReporterTest { - private final int PORT = 4434; + private static final int PORT = 4434; - private StatsDClient statsd; private StatsdReporter reporter; @Test @@ -55,7 +54,7 @@ public void statsdClient() { Thread serverThread = new Thread(server); serverThread.start(); - statsd = new NonBlockingStatsDClient("statsd-test", "localhost", PORT); + StatsDClient statsd = new NonBlockingStatsDClient("statsd-test", "localhost", PORT); reporter = new StatsdReporter(statsd); reporter.reportCounter("statsd-count", null, 4);