From 15e3d138868290cc2cdcfc7c04053872be7254d3 Mon Sep 17 00:00:00 2001 From: Brian Harrington Date: Fri, 18 Jul 2014 13:34:44 -0700 Subject: [PATCH] initial version --- README.md | 106 +++- build.gradle | 139 +++++ codequality/HEADER | 13 + codequality/checkstyle.xml | 191 ++++++ .../japi-checker-cli-0.2.0-SNAPSHOT.jar | Bin 0 -> 268018 bytes codequality/spectator-api-BASELINE.jar | Bin 0 -> 41994 bytes gradle.properties | 1 + gradle/buildscript.gradle | 11 + gradle/check.gradle | 34 ++ gradle/convention.gradle | 101 ++++ gradle/license.gradle | 10 + gradle/maven.gradle | 70 +++ gradle/netflix-oss.gradle | 1 + gradle/release.gradle | 61 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 46742 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 164 ++++++ settings.gradle | 6 + .../netflix/spectator/api/AbstractMeter.java | 63 ++ .../spectator/api/AbstractRegistry.java | 219 +++++++ .../com/netflix/spectator/api/AggrMeter.java | 77 +++ .../java/com/netflix/spectator/api/Clock.java | 52 ++ .../com/netflix/spectator/api/Config.java | 52 ++ .../com/netflix/spectator/api/Counter.java | 35 ++ .../netflix/spectator/api/DefaultCounter.java | 72 +++ .../api/DefaultDistributionSummary.java | 83 +++ .../com/netflix/spectator/api/DefaultId.java | 122 ++++ .../spectator/api/DefaultLongTaskTimer.java | 112 ++++ .../spectator/api/DefaultRegistry.java | 48 ++ .../netflix/spectator/api/DefaultTimer.java | 110 ++++ .../spectator/api/DistributionSummary.java | 43 ++ .../netflix/spectator/api/DoubleFunction.java | 40 ++ .../spectator/api/ExtendedRegistry.java | 549 ++++++++++++++++++ .../com/netflix/spectator/api/Functions.java | 113 ++++ .../java/com/netflix/spectator/api/Gauge.java | 25 + .../java/com/netflix/spectator/api/Id.java | 33 ++ .../netflix/spectator/api/LongTaskTimer.java | 55 ++ .../netflix/spectator/api/ManualClock.java | 68 +++ .../netflix/spectator/api/Measurement.java | 75 +++ .../java/com/netflix/spectator/api/Meter.java | 39 ++ .../netflix/spectator/api/NoopCounter.java | 58 ++ .../api/NoopDistributionSummary.java | 60 ++ .../com/netflix/spectator/api/NoopId.java | 57 ++ .../netflix/spectator/api/NoopRegistry.java | 90 +++ .../com/netflix/spectator/api/NoopTimer.java | 73 +++ .../netflix/spectator/api/ObjectGauge.java | 57 ++ .../com/netflix/spectator/api/Registry.java | 99 ++++ .../spectator/api/RegistryListener.java | 25 + .../com/netflix/spectator/api/Spectator.java | 91 +++ .../java/com/netflix/spectator/api/Tag.java | 25 + .../com/netflix/spectator/api/TagList.java | 151 +++++ .../com/netflix/spectator/api/Throwables.java | 59 ++ .../java/com/netflix/spectator/api/Timer.java | 66 +++ .../netflix/spectator/api/ValueFunction.java | 31 + .../netflix/spectator/api/package-info.java | 83 +++ .../netflix/spectator/impl/Preconditions.java | 46 ++ .../spectator/api/DefaultCounterTest.java | 63 ++ .../api/DefaultDistributionSummaryTest.java | 60 ++ .../netflix/spectator/api/DefaultIdTest.java | 98 ++++ .../api/DefaultLongTaskTimerTest.java | 95 +++ .../spectator/api/DefaultRegistryTest.java | 267 +++++++++ .../spectator/api/DefaultTimerTest.java | 131 +++++ .../spectator/api/ExtendedRegistryTest.java | 192 ++++++ .../netflix/spectator/api/FunctionsTest.java | 135 +++++ .../spectator/api/MeasurementTest.java | 42 ++ .../spectator/api/NoopCounterTest.java | 52 ++ .../api/NoopDistributionSummaryTest.java | 46 ++ .../com/netflix/spectator/api/NoopIdTest.java | 39 ++ .../spectator/api/NoopRegistryTest.java | 126 ++++ .../netflix/spectator/api/NoopTimerTest.java | 48 ++ .../spectator/api/ObjectGaugeTest.java | 46 ++ .../netflix/spectator/api/SpectatorTest.java | 41 ++ .../netflix/spectator/api/TagListTest.java | 91 +++ .../netflix/spectator/gc/CircularBuffer.java | 64 ++ .../com/netflix/spectator/gc/GcEvent.java | 119 ++++ .../netflix/spectator/gc/GcEventListener.java | 22 + .../com/netflix/spectator/gc/GcLogger.java | 220 +++++++ .../java/com/netflix/spectator/gc/GcType.java | 30 + .../netflix/spectator/gc/HelperFunctions.java | 89 +++ .../nflx/ChronosGcEventListener.java | 131 +++++ .../com/netflix/spectator/nflx/Plugin.java | 51 ++ .../spectator/ribbon/MeteredRestClient.java | 98 ++++ .../spectator/ribbon/RestClientFactory.java | 49 ++ .../ribbon/RibbonClientConfigImpl.java | 39 ++ .../src/main/resources/spectator.properties | 32 + .../ribbon/MeteredRestClientTest.java | 133 +++++ .../spectator/metrics2/MetricsCounter.java | 76 +++ .../metrics2/MetricsDistributionSummary.java | 75 +++ .../spectator/metrics2/MetricsRegistry.java | 72 +++ .../spectator/metrics2/MetricsTimer.java | 101 ++++ .../com.netflix.spectator.api.Registry | 1 + .../spectator/metrics3/MetricsCounter.java | 76 +++ .../metrics3/MetricsDistributionSummary.java | 80 +++ .../spectator/metrics3/MetricsRegistry.java | 65 +++ .../spectator/metrics3/MetricsTimer.java | 104 ++++ .../com.netflix.spectator.api.Registry | 1 + .../netflix/spectator/servo/ServoCounter.java | 90 +++ .../servo/ServoDistributionSummary.java | 102 ++++ .../com/netflix/spectator/servo/ServoId.java | 80 +++ .../netflix/spectator/servo/ServoMeter.java | 24 + .../spectator/servo/ServoRegistry.java | 118 ++++ .../com/netflix/spectator/servo/ServoTag.java | 41 ++ .../netflix/spectator/servo/ServoTimer.java | 128 ++++ .../com.netflix.spectator.api.Registry | 1 + 104 files changed, 7951 insertions(+), 3 deletions(-) create mode 100755 build.gradle create mode 100644 codequality/HEADER create mode 100644 codequality/checkstyle.xml create mode 100644 codequality/japi-checker-cli-0.2.0-SNAPSHOT.jar create mode 100644 codequality/spectator-api-BASELINE.jar create mode 100644 gradle.properties create mode 100644 gradle/buildscript.gradle create mode 100644 gradle/check.gradle create mode 100644 gradle/convention.gradle create mode 100644 gradle/license.gradle create mode 100644 gradle/maven.gradle create mode 100644 gradle/netflix-oss.gradle create mode 100644 gradle/release.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100755 settings.gradle create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/AbstractMeter.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/AbstractRegistry.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/AggrMeter.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Clock.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Config.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Counter.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/DefaultCounter.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/DefaultDistributionSummary.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/DefaultId.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/DefaultLongTaskTimer.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/DefaultRegistry.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/DefaultTimer.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/DistributionSummary.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/DoubleFunction.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/ExtendedRegistry.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Functions.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Gauge.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Id.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/LongTaskTimer.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/ManualClock.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Measurement.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Meter.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/NoopCounter.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/NoopDistributionSummary.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/NoopId.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/NoopRegistry.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/NoopTimer.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/ObjectGauge.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Registry.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/RegistryListener.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Spectator.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Tag.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/TagList.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Throwables.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/Timer.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/ValueFunction.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/api/package-info.java create mode 100644 spectator-api/src/main/java/com/netflix/spectator/impl/Preconditions.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/DefaultCounterTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/DefaultDistributionSummaryTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/DefaultIdTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/DefaultLongTaskTimerTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/DefaultRegistryTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/DefaultTimerTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/ExtendedRegistryTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/FunctionsTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/MeasurementTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/NoopCounterTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/NoopDistributionSummaryTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/NoopIdTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/NoopRegistryTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/NoopTimerTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/ObjectGaugeTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/SpectatorTest.java create mode 100644 spectator-api/src/test/java/com/netflix/spectator/api/TagListTest.java create mode 100644 spectator-ext-gc/src/main/java/com/netflix/spectator/gc/CircularBuffer.java create mode 100644 spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcEvent.java create mode 100644 spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcEventListener.java create mode 100644 spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcLogger.java create mode 100644 spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcType.java create mode 100644 spectator-ext-gc/src/main/java/com/netflix/spectator/gc/HelperFunctions.java create mode 100644 spectator-nflx/src/main/java/com/netflix/spectator/nflx/ChronosGcEventListener.java create mode 100644 spectator-nflx/src/main/java/com/netflix/spectator/nflx/Plugin.java create mode 100644 spectator-nflx/src/main/java/com/netflix/spectator/ribbon/MeteredRestClient.java create mode 100644 spectator-nflx/src/main/java/com/netflix/spectator/ribbon/RestClientFactory.java create mode 100644 spectator-nflx/src/main/java/com/netflix/spectator/ribbon/RibbonClientConfigImpl.java create mode 100644 spectator-nflx/src/main/resources/spectator.properties create mode 100644 spectator-nflx/src/test/java/com/netflix/spectator/ribbon/MeteredRestClientTest.java create mode 100644 spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsCounter.java create mode 100644 spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsDistributionSummary.java create mode 100644 spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsRegistry.java create mode 100644 spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsTimer.java create mode 100644 spectator-reg-metrics2/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry create mode 100644 spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsCounter.java create mode 100644 spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsDistributionSummary.java create mode 100644 spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsRegistry.java create mode 100644 spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsTimer.java create mode 100644 spectator-reg-metrics3/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry create mode 100644 spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoCounter.java create mode 100644 spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoDistributionSummary.java create mode 100644 spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoId.java create mode 100644 spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoMeter.java create mode 100644 spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoRegistry.java create mode 100644 spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoTag.java create mode 100644 spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoTimer.java create mode 100644 spectator-reg-servo/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry diff --git a/README.md b/README.md index a2993a73c..1af53fd22 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,104 @@ -spectator -========= -tbd +# Spectator + +Simple library for instrumenting code to record dimensional time series. + +## Requirements + +* Java 7 or higher. + +## Dependencies + +To instrument your code you need to depend on the api library. This provides the minimal interfaces +for you to code against and build test cases. The only dependency is slf4j. + +``` +com.netflix.spectator:spectator-api:latest.release +``` + +If running at Netflix with the standard platform, use the spectator-nflx library to automatically +setup and initalize with the correct bindings to report data to internal tools like Atlas and +Chronos. + +``` +com.netflix.spectator:spectator-nflx:latest.release +``` + +## Instrumenting Code + +Suppose we have a server and we want to keep track of: + +* Number of requests received with dimensions for breaking down by status code, country, and + the exception type if the request fails in an unexpected way. +* Latency for handling requests. +* Summary of the response sizes. +* Current number of active connections on the server. + +Here is some sample code that does that: + +```java +// The Spectator class provides a static lookup for the default registry +Server s = new Server(Spectator.registry()); + +public class Server { + private final ExtendedRegistry registry; + private final Id requestCountId; + private final Timer requestLatency; + private final DistributionSummary responseSizes; + + // We can pass in the registry to make unit testing easier + public Server(ExtendedRegistry registry) { + this.registry = registry; + + // Create a base id for the request count. The id will get refined with additional dimensions + // when we receive a request. + requestCountId = registry.createId("server.requestCount"); + + // Create a timer for tracking the latency. The reference can be held onto to avoid + // additional lookup cost in critical paths. + requestLatency = registry.timer("server.requestLatency"); + + // Create a distribution summary meter for tracking the response sizes. + responseSizes = registry.distributionSummary("server.responseSizes"); + + // Gauge type that can be sampled. In this case it will invoke the specified method via + // reflection to get the value. The registry will keep a weak reference to the object passed + // in so that registration will not prevent garbage collection of the server object. + registry.methodValue("server.numConnections", this, "getNumConnections"); + } + + public Response handle(Request req) { + final long s = System.nanoTime(); + try { + Response res = doSomething(req); + + // Update the counter id with dimensions based on the request. The counter will then + // be looked up in the registry which should be fairly cheap, such as lookup of id object + // in a ConcurrentHashMap, it is more expensive than having a local variable set to the + // counter. + final Id cntId = requestCountId + .withTag("country", req.country()) + .withTag("status", res.status()); + registry.counter(cntId).increment(); + + responseSizes.record(res.body().size()); + + return res; + } catch (Exception e) { + final Id cntId = requestCountId + .withTag("country", req.country()) + .withTag("status", "exception") + .withTag("error", e.getClass().getSimpleName()); + registry.counter(cntId).increment(); + throw e; + } finally { + // Update the latency timer. This should typically be done in a finally block. + requestLatency.record(System.nanoTime() - s, TimeUnit.NANOSECONDS); + } + } + + public int getNumConnections() { + // however we determine the current number of connections on the server + } +} +``` diff --git a/build.gradle b/build.gradle new file mode 100755 index 000000000..ff9ee0467 --- /dev/null +++ b/build.gradle @@ -0,0 +1,139 @@ + +// Establish version and status +ext.githubProjectName = 'spectator' + +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + apply from: file('gradle/buildscript.gradle'), to: buildscript +} + +allprojects { + apply plugin: 'java' + apply plugin: 'jacoco' + apply plugin: 'idea' + + repositories { + mavenLocal() + mavenCentral() + } + + jacoco { + toolVersion = "0.7.1.201405082137" + } +} + +apply from: file('gradle/convention.gradle') +apply from: file('gradle/maven.gradle') +apply from: file('gradle/check.gradle') +apply from: file('gradle/license.gradle') +apply from: file('gradle/release.gradle') + +subprojects { + group = "com.netflix.${githubProjectName}" + + sourceCompatibility = 1.7 + targetCompatibility = 1.7 + + tasks.withType(Compile) { + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" + } + + javadoc { + options { + links = ['http://docs.oracle.com/javase/7/docs/api/'] + } + } + + dependencies { + compile 'org.slf4j:slf4j-api:1.7.6' + testCompile 'junit:junit:4.10' + testCompile 'nl.jqno.equalsverifier:equalsverifier:1.4.1' + } + + jacocoTestReport { + additionalSourceDirs = files(sourceSets.main.allJava.srcDirs) + reports { + xml.enabled false + csv.enabled false + html.destination "${buildDir}/reports/jacoco" + } + } + + task copyDepsToBuild << { + ['compile', 'runtime', 'testCompile', 'testRuntime'].each { conf -> + delete "${buildDir}/dependencies/${conf}" + copy { + from configurations[conf] + into "${buildDir}/dependencies/${conf}" + } + } + } +} + +project(':spectator-api') { + + javadoc { + options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED + title "Spectator" + } + + task(checkCompatibility, dependsOn: 'jar', type: JavaExec) { + // There is probably a better way, but we remove "-SNAPSHOT" from the name if the archive + // doesn't exist because release plugin changes the name. + main = 'com.googlecode.japi.checker.cli.Main' + classpath = files("$projectDir/../codequality/japi-checker-cli-0.2.0-SNAPSHOT.jar") + args = [ + "$projectDir/../codequality/spectator-api-BASELINE.jar", + (jar.archivePath.exists()) + ? jar.archivePath.path + : jar.archivePath.path.replace("-SNAPSHOT", "") + ] + } + + build { + it.dependsOn checkCompatibility + } +} + +project(':spectator-nflx') { + dependencies { + compile project(':spectator-api') + compile project(':spectator-ext-gc') + compile project(':spectator-reg-servo') + compile 'com.netflix.archaius:archaius-core:latest.release' + compile 'com.netflix.governator:governator:latest.release' + compile 'com.netflix.ribbon:ribbon-core:0.3.+' + compile 'com.netflix.ribbon:ribbon-eureka:0.3.+' + compile 'com.netflix.ribbon:ribbon-httpclient:0.3.+' + } +} + +project(':spectator-ext-gc') { + dependencies { + compile project(':spectator-api') + } +} + +project(':spectator-reg-servo') { + dependencies { + compile project(':spectator-api') + compile 'com.netflix.servo:servo-core:latest.release' + } +} + +project(':spectator-reg-metrics2') { + dependencies { + compile project(':spectator-api') + compile 'com.yammer.metrics:metrics-core:2.2.0' + } +} + +project(':spectator-reg-metrics3') { + dependencies { + compile project(':spectator-api') + compile 'com.codahale.metrics:metrics-core:3.0.2' + } +} diff --git a/codequality/HEADER b/codequality/HEADER new file mode 100644 index 000000000..3102e4b44 --- /dev/null +++ b/codequality/HEADER @@ -0,0 +1,13 @@ +Copyright ${year} Netflix, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/codequality/checkstyle.xml b/codequality/checkstyle.xml new file mode 100644 index 000000000..6b0ec7011 --- /dev/null +++ b/codequality/checkstyle.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/codequality/japi-checker-cli-0.2.0-SNAPSHOT.jar b/codequality/japi-checker-cli-0.2.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..e335bf7edb477f533877910a6cba17b2023870b0 GIT binary patch literal 268018 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 z∋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

Q3WX;x?P| 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 W4UvLSa2d2Qcpip#Q8SO_3H}EJlqPuq literal 0 HcmV?d00001 diff --git a/codequality/spectator-api-BASELINE.jar b/codequality/spectator-api-BASELINE.jar new file mode 100644 index 0000000000000000000000000000000000000000..872bce985e89c1964dc096c36de6923c372a7264 GIT binary patch literal 41994 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 + apply plugin: 'java' // Plugin as major conventions + + sourceCompatibility = 1.6 + + // Restore status after Java plugin + status = rootProject.status + + task sourcesJar(type: Jar, dependsOn:classes) { + from sourceSets.main.allSource + classifier 'sources' + extension 'jar' + } + + task javadocJar(type: Jar, dependsOn:javadoc) { + from javadoc.destinationDir + classifier 'javadoc' + extension 'jar' + } + + configurations.add('sources') + configurations.add('javadoc') + configurations.archives { + extendsFrom configurations.sources + extendsFrom configurations.javadoc + } + + // When outputting to an Ivy repo, we want to use the proper type field + gradle.taskGraph.whenReady { + def isNotMaven = !it.hasTask(project.uploadMavenCentral) + if (isNotMaven) { + def artifacts = project.configurations.sources.artifacts + def sourceArtifact = artifacts.iterator().next() + sourceArtifact.type = 'sources' + } + } + + artifacts { + sources(sourcesJar) { + // Weird Gradle quirk where type will be used for the extension, but only for sources + type 'jar' + } + javadoc(javadocJar) { + type 'javadoc' + } + } + + configurations { + provided { + description = 'much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive.' + transitive = true + visible = true + } + } + + project.sourceSets { + main.compileClasspath += project.configurations.provided + main.runtimeClasspath -= project.configurations.provided + test.compileClasspath += project.configurations.provided + test.runtimeClasspath += project.configurations.provided + } +} + +apply plugin: 'github-pages' // Used to create publishGhPages task + +def docTasks = [:] +[Javadoc,ScalaDoc,Groovydoc].each{ Class docClass -> + def allSources = allprojects.tasks*.withType(docClass).flatten()*.source + if (allSources) { + def shortName = docClass.simpleName.toLowerCase() + def docTask = task "aggregate${shortName.capitalize()}"(type: docClass, description: "Aggregate subproject ${shortName}s") { + source = allSources + destinationDir = file("${project.buildDir}/docs/${shortName}") + doFirst { + def classpaths = allprojects.findAll { it.plugins.hasPlugin(JavaPlugin) }.collect { it.sourceSets.main.compileClasspath } + classpath = files(classpaths) + } + } + docTasks[shortName] = docTask + processGhPages.dependsOn(docTask) + } +} + +githubPages { + repoUri = "git@github.com:Netflix/${rootProject.githubProjectName}.git" + pages { + docTasks.each { shortName, docTask -> + from(docTask.outputs.files) { + into "docs/${shortName}" + } + } + } +} + +// Generate wrapper, which is distributed as part of source to alleviate the need of installing gradle +task createWrapper(type: Wrapper) { + gradleVersion = '1.5' +} diff --git a/gradle/license.gradle b/gradle/license.gradle new file mode 100644 index 000000000..abd2e2c0e --- /dev/null +++ b/gradle/license.gradle @@ -0,0 +1,10 @@ +// Dependency for plugin was set in buildscript.gradle + +subprojects { +apply plugin: 'license' //nl.javadude.gradle.plugins.license.LicensePlugin +license { + header rootProject.file('codequality/HEADER') + ext.year = Calendar.getInstance().get(Calendar.YEAR) + skipExistingHeaders true +} +} diff --git a/gradle/maven.gradle b/gradle/maven.gradle new file mode 100644 index 000000000..817846d77 --- /dev/null +++ b/gradle/maven.gradle @@ -0,0 +1,70 @@ +// Maven side of things +subprojects { + apply plugin: 'maven' // Java plugin has to have been already applied for the conf2scope mappings to work + apply plugin: 'signing' + + signing { + required { gradle.taskGraph.hasTask(uploadMavenCentral) } + sign configurations.archives + } + +/** + * Publishing to Maven Central example provided from http://jedicoder.blogspot.com/2011/11/automated-gradle-project-deployment-to.html + * artifactory will execute uploadArchives to force generation of ivy.xml, and we don't want that to trigger an upload to maven + * central, so using custom upload task. + */ +task uploadMavenCentral(type:Upload, dependsOn: signArchives) { + configuration = configurations.archives + onlyIf { ['release', 'snapshot'].contains(project.status) } + repositories.mavenDeployer { + beforeDeployment { signing.signPom(it) } + + // To test deployment locally, use the following instead of oss.sonatype.org + //repository(url: "file://localhost/${rootProject.rootDir}/repo") + + def sonatypeUsername = rootProject.hasProperty('sonatypeUsername')?rootProject.sonatypeUsername:'' + def sonatypePassword = rootProject.hasProperty('sonatypePassword')?rootProject.sonatypePassword:'' + + repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2') { + authentication(userName: sonatypeUsername, password: sonatypePassword) + } + + snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots/') { + authentication(userName: sonatypeUsername, password: sonatypePassword) + } + + // Prevent datastamp from being appending to artifacts during deployment + uniqueVersion = false + + // Closure to configure all the POM with extra info, common to all projects + pom.project { + name "${project.name}" + description "${project.name} developed by Netflix" + developers { + developer { + id 'netflixgithub' + name 'Netflix Open Source Development' + email 'talent@netflix.com' + } + } + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + distribution 'repo' + } + } + url "https://github.com/Netflix/${rootProject.githubProjectName}" + scm { + connection "scm:git:git@github.com:Netflix/${rootProject.githubProjectName}.git" + url "scm:git:git@github.com:Netflix/${rootProject.githubProjectName}.git" + developerConnection "scm:git:git@github.com:Netflix/${rootProject.githubProjectName}.git" + } + issueManagement { + system 'github' + url "https://github.com/Netflix/${rootProject.githubProjectName}/issues" + } + } + } + } +} diff --git a/gradle/netflix-oss.gradle b/gradle/netflix-oss.gradle new file mode 100644 index 000000000..a87bc54ef --- /dev/null +++ b/gradle/netflix-oss.gradle @@ -0,0 +1 @@ +apply from: 'http://artifacts.netflix.com/gradle-netflix-local/artifactory.gradle' diff --git a/gradle/release.gradle b/gradle/release.gradle new file mode 100644 index 000000000..69027db83 --- /dev/null +++ b/gradle/release.gradle @@ -0,0 +1,61 @@ +apply plugin: 'release' + +[ uploadIvyLocal: 'uploadLocal', uploadArtifactory: 'artifactoryPublish', buildWithArtifactory: 'build' ].each { key, value -> + // Call out to compile against internal repository + task "${key}"(type: GradleBuild) { + startParameter = project.gradle.startParameter.newInstance() + doFirst { + startParameter.projectProperties = [status: project.status, preferredStatus: project.status] + } + startParameter.addInitScript( file('gradle/netflix-oss.gradle') ) + startParameter.getExcludedTaskNames().add('check') + tasks = [ 'build', value ] + } +} + +// Marker task for following code to key in on +task releaseCandidate(dependsOn: release) +task forceCandidate { + onlyIf { gradle.taskGraph.hasTask(releaseCandidate) } + doFirst { project.status = 'candidate' } +} +task forceRelease { + onlyIf { !gradle.taskGraph.hasTask(releaseCandidate) } + doFirst { project.status = 'release' } +} +release.dependsOn([forceCandidate, forceRelease]) + +task uploadMavenCentral(dependsOn: subprojects.tasks.uploadMavenCentral) +task releaseSnapshot(dependsOn: [uploadArtifactory, uploadMavenCentral]) + +// Ensure our versions look like the project status before publishing +task verifyStatus << { + def hasSnapshot = version.contains('-SNAPSHOT') + if (project.status == 'snapshot' && !hasSnapshot) { + throw new GradleException("Version (${version}) needs -SNAPSHOT if publishing snapshot") + } +} +uploadArtifactory.dependsOn(verifyStatus) +uploadMavenCentral.dependsOn(verifyStatus) + +// Ensure upload happens before tagging, hence upload failures will leave repo in a revertable state +preTagCommit.dependsOn([uploadArtifactory, uploadMavenCentral]) + + +gradle.taskGraph.whenReady { taskGraph -> + def hasRelease = taskGraph.hasTask('commitNewVersion') + def indexOf = { return taskGraph.allTasks.indexOf(it) } + + if (hasRelease) { + assert indexOf(build) < indexOf(unSnapshotVersion), 'build target has to be after unSnapshotVersion' + assert indexOf(uploadMavenCentral) < indexOf(preTagCommit), 'preTagCommit has to be after uploadMavenCentral' + assert indexOf(uploadArtifactory) < indexOf(preTagCommit), 'preTagCommit has to be after uploadArtifactory' + } +} + +// Prevent plugin from asking for a version number interactively +ext.'gradle.release.useAutomaticVersion' = "true" + +release { + git.requireBranch = null +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..faa569a9a0eedc9ff37450fed24a7efd77a86729 GIT binary patch literal 46742 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/settings.gradle b/settings.gradle new file mode 100755 index 000000000..5ede2af1b --- /dev/null +++ b/settings.gradle @@ -0,0 +1,6 @@ +include 'spectator-api', + 'spectator-nflx', + 'spectator-ext-gc', + 'spectator-reg-metrics2', + 'spectator-reg-metrics3', + 'spectator-reg-servo' diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/AbstractMeter.java b/spectator-api/src/main/java/com/netflix/spectator/api/AbstractMeter.java new file mode 100644 index 000000000..60f7bc893 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/AbstractMeter.java @@ -0,0 +1,63 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.lang.ref.WeakReference; + +/** + * Helper base class for meters that maintains a weak reference to the object being measured. + * Meters that are based on polling rather than activity need some other way to define the + * lifespan and in this case that is if the reference still exists. Basing this off a weak + * reference means there doesn't need to be an explicit de-registration and registering to + * collect insight will not prevent garbage collection of the underlying object. + */ +public abstract class AbstractMeter implements Meter { + /** Clock to use for getting measurement timestamps. */ + protected final Clock clock; + + /** Identifier for the meter. */ + protected final Id id; + + /** Reference to the underlying object used to compute the measurements. */ + protected final WeakReference ref; + + /** + * Create a new instance. + * + * @param clock + * Clock to use for getting measurement timestamps. Typically should be the clock used by + * the registry (see {@link Registry#clock()}). + * @param id + * Identifier for the meter. + * @param obj + * Reference to the underlying object used to compute the measurements. + */ + public AbstractMeter(Clock clock, Id id, T obj) { + this.clock = clock; + this.id = id; + this.ref = new WeakReference<>(obj); + } + + /** {@inheritDoc} */ + @Override public Id id() { + return this.id; + } + + /** {@inheritDoc} */ + @Override public boolean hasExpired() { + return this.ref.get() == null; + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/AbstractRegistry.java b/spectator-api/src/main/java/com/netflix/spectator/api/AbstractRegistry.java new file mode 100644 index 000000000..01af6788c --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/AbstractRegistry.java @@ -0,0 +1,219 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Base class to make it easier to implement a simple registry that only needs to customise the + * types returned for Counter, DistributionSummary, and Timer calls. + */ +public abstract class AbstractRegistry implements Registry { + + private final Clock clock; + + private final ConcurrentHashMap listeners; + private final ConcurrentHashMap meters; + + /** + * Create a new instance. + * + * @param clock + * Clock used for performing all timing measurements. + */ + public AbstractRegistry(Clock clock) { + this.clock = clock; + listeners = new ConcurrentHashMap<>(); + meters = new ConcurrentHashMap<>(); + } + + /** + * Create a new counter instance for a given id. + * + * @param id + * Identifier used to lookup this meter in the registry. + * @return + * New counter instance. + */ + protected abstract Counter newCounter(Id id); + + /** + * Create a new distribution summary instance for a given id. + * + * @param id + * Identifier used to lookup this meter in the registry. + * @return + * New counter instance. + */ + protected abstract DistributionSummary newDistributionSummary(Id id); + + /** + * Create a new timer instance for a given id. + * + * @param id + * Identifier used to lookup this meter in the registry. + * @return + * New counter instance. + */ + protected abstract Timer newTimer(Id id); + + /** {@inheritDoc} */ + @Override + public final Clock clock() { + return clock; + } + + /** {@inheritDoc} */ + @Override + public final Id createId(String name) { + return new DefaultId(name, TagList.EMPTY); + } + + /** {@inheritDoc} */ + @Override + public final Id createId(String name, Iterable tags) { + return new DefaultId(name, TagList.create(tags)); + } + + private void logTypeError(Id id, Class desired, Class found) { + final String dtype = desired.getName(); + final String ftype = found.getName(); + final String msg = String.format("cannot access '%s' as a %s, it already exists as a %s", + id, dtype, ftype); + Throwables.propagate(new IllegalStateException(msg)); + } + + private void addToAggr(Meter aggr, Meter meter) { + if (aggr instanceof AggrMeter) { + ((AggrMeter) aggr).add(meter); + } else { + logTypeError(meter.id(), meter.getClass(), aggr.getClass()); + } + } + + private Meter putIfAbsent(Id id, Meter m, Meter fallback) { + return (meters.size() >= Config.maxNumberOfMeters()) ? fallback : meters.putIfAbsent(id, m); + } + + /** {@inheritDoc} */ + @Override + public final void register(Meter meter) { + Meter m = meters.get(meter.id()); + if (m == null) { + if (meters.size() >= Config.maxNumberOfMeters()) { + return; + } + AggrMeter aggr = new AggrMeter(meter.id()); + m = meters.putIfAbsent(meter.id(), aggr); + if (m == null) { + aggr.add(meter); + } else { + addToAggr(m, meter); + } + } else { + addToAggr(m, meter); + } + notifyOfAdd(meter); + } + + /** {@inheritDoc} */ + @Override + public final Counter counter(Id id) { + Meter m = meters.get(id); + if (m == null) { + Counter c = newCounter(id); + m = putIfAbsent(id, c, NoopCounter.INSTANCE); + if (m == null) { + m = c; + notifyOfAdd(m); + } + } + if (!(m instanceof Counter)) { + logTypeError(id, Counter.class, m.getClass()); + m = NoopCounter.INSTANCE; + } + return (Counter) m; + } + + /** {@inheritDoc} */ + @Override + public final DistributionSummary distributionSummary(Id id) { + Meter m = meters.get(id); + if (m == null) { + DistributionSummary s = newDistributionSummary(id); + m = putIfAbsent(id, s, NoopDistributionSummary.INSTANCE); + if (m == null) { + m = s; + notifyOfAdd(m); + } + } + if (!(m instanceof DistributionSummary)) { + logTypeError(id, DistributionSummary.class, m.getClass()); + m = NoopDistributionSummary.INSTANCE; + } + return (DistributionSummary) m; + } + + /** {@inheritDoc} */ + @Override + public final Timer timer(Id id) { + Meter m = meters.get(id); + if (m == null) { + Timer t = newTimer(id); + m = putIfAbsent(id, t, NoopTimer.INSTANCE); + if (m == null) { + m = t; + notifyOfAdd(m); + } + } + if (!(m instanceof Timer)) { + logTypeError(id, Timer.class, m.getClass()); + m = NoopTimer.INSTANCE; + } + return (Timer) m; + } + + /** {@inheritDoc} */ + @Override + public final Meter get(Id id) { + return meters.get(id); + } + + /** {@inheritDoc} */ + @Override + public final Iterator iterator() { + return meters.values().iterator(); + } + + /** {@inheritDoc} */ + @Override + public final void addListener(RegistryListener listener) { + listeners.put(listener, listener); + } + + /** {@inheritDoc} */ + @Override + public final void removeListener(RegistryListener listener) { + listeners.remove(listener); + } + + private void notifyOfAdd(Meter meter) { + for (RegistryListener listener : listeners.values()) { + listener.onAdd(meter); + } + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/AggrMeter.java b/spectator-api/src/main/java/com/netflix/spectator/api/AggrMeter.java new file mode 100644 index 000000000..6c144d539 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/AggrMeter.java @@ -0,0 +1,77 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * Composite meter that computes a sum aggregate of the overlapping measurements in meters in the + * set. This is typically used to combine the values of gauges that share the same id. + */ +class AggrMeter implements Meter { + private final Id id; + private final ConcurrentLinkedQueue queue; + + /** Create a new instance. */ + AggrMeter(Id id) { + this.id = id; + this.queue = new ConcurrentLinkedQueue<>(); + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + Map measurements = new HashMap<>(); + Iterator iter = queue.iterator(); + while (iter.hasNext()) { + Meter meter = iter.next(); + if (meter.hasExpired()) { + iter.remove(); + } else { + for (Measurement m : meter.measure()) { + Measurement prev = measurements.get(m.id()); + if (prev == null) { + measurements.put(m.id(), m); + } else { + double v = prev.value() + m.value(); + measurements.put(prev.id(), new Measurement(prev.id(), prev.timestamp(), v)); + } + } + } + } + return measurements.values(); + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return queue.isEmpty(); + } + + /** Adds a meter to the set included in the aggregate. */ + void add(Meter m) { + queue.add(m); + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Clock.java b/spectator-api/src/main/java/com/netflix/spectator/api/Clock.java new file mode 100644 index 000000000..66222fdef --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Clock.java @@ -0,0 +1,52 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** + * A timing source that can be used to access the current wall time as well as a high resolution + * monotonic time to measuring elapsed times. Most of the time the {@link #SYSTEM} implementation + * that calls the builtin java methods is probably the right one to use. Other implementations + * would typically only get used for unit tests or other cases where precise control of the clock + * is needed. + */ +public interface Clock { + /** + * Current wall time in milliseconds since the epoch. Typically equivalent to + * System.currentTimeMillis. + */ + long wallTime(); + + /** + * Current time from a monotonic clock source. The value is only meaningful when compared with + * another snapshot to determine the elapsed time for an operation. The difference between two + * samples will have a unit of nanoseconds. The returned value is typically equivalent to + * System.nanoTime. + */ + long monotonicTime(); + + /** + * Default clock implementation based on corresponding calls in {@link java.lang.System}. + */ + Clock SYSTEM = new Clock() { + public long wallTime() { + return System.currentTimeMillis(); + } + + public long monotonicTime() { + return System.nanoTime(); + } + }; +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Config.java b/spectator-api/src/main/java/com/netflix/spectator/api/Config.java new file mode 100644 index 000000000..a32578b4d --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Config.java @@ -0,0 +1,52 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** Helper methods for accessing configuration settings. */ +final class Config { + + private static final String PREFIX = "spectator.api."; + + /** Value for registry class to explicitly indicate the service loader should be used. */ + static final String SERVICE_LOADER = "service-loader"; + + private Config() { + } + + /** Should an exception be thrown for warnings? */ + static boolean propagateWarnings() { + return Boolean.valueOf(System.getProperty(PREFIX + "propagateWarnings", "false")); + } + + /** Class implementing the {@link Registry} interface that should be loaded. */ + static String registryClass() { + return System.getProperty(PREFIX + "registryClass", SERVICE_LOADER); + } + + /** + * For classes based on {@link AbstractRegistry} this setting is used to determine the maximum + * number of registered meters permitted. This limit is used to help protect the system from a + * memory leak if there is a bug or irresponsible usage of registering meters. + * + * @return + * Maximum number of distinct meters that can be registered at a given time. The default is + * {@link java.lang.Integer#MAX_VALUE}. + */ + static int maxNumberOfMeters() { + final String v = System.getProperty(PREFIX + "maxNumberOfMeters"); + return (v == null) ? Integer.MAX_VALUE : Integer.parseInt(v); + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Counter.java b/spectator-api/src/main/java/com/netflix/spectator/api/Counter.java new file mode 100644 index 000000000..36643cd7d --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Counter.java @@ -0,0 +1,35 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** + * Measures the rate of change based on calls to increment. + */ +public interface Counter extends Meter { + /** Update the counter by one. */ + void increment(); + + /** + * Update the counter by {@code amount}. + * + * @param amount + * Amount to add to the counter. + */ + void increment(long amount); + + /** The cumulative count since this counter was created. */ + long count(); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/DefaultCounter.java b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultCounter.java new file mode 100644 index 000000000..23646d424 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultCounter.java @@ -0,0 +1,72 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.Collections; +import java.util.concurrent.atomic.AtomicLong; + +/** Counter implementation for the default registry. */ +final class DefaultCounter implements Counter { + + private final Clock clock; + private final Id id; + private final AtomicLong count; + + /** Create a new instance. */ + DefaultCounter(Clock clock, Id id) { + this.clock = clock; + this.id = id; + this.count = new AtomicLong(0L); + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + long now = clock.wallTime(); + long v = count.get(); + return Collections.singleton(new Measurement(id, now, v)); + } + + /** {@inheritDoc} */ + @Override + public void increment() { + count.incrementAndGet(); + } + + /** {@inheritDoc} */ + @Override + public void increment(long amount) { + count.addAndGet(amount); + } + + /** {@inheritDoc} */ + @Override + public long count() { + return count.get(); + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/DefaultDistributionSummary.java b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultDistributionSummary.java new file mode 100644 index 000000000..c45ec0ea8 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultDistributionSummary.java @@ -0,0 +1,83 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +/** Distribution summary implementation for the default registry. */ +final class DefaultDistributionSummary implements DistributionSummary { + + private final Clock clock; + private final Id id; + private final AtomicLong count; + private final AtomicLong totalAmount; + + private final Id countId; + private final Id totalAmountId; + + /** Create a new instance. */ + DefaultDistributionSummary(Clock clock, Id id) { + this.clock = clock; + this.id = id; + count = new AtomicLong(0L); + totalAmount = new AtomicLong(0L); + countId = id.withTag("statistic", "count"); + totalAmountId = id.withTag("statistic", "totalAmount"); + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void record(long amount) { + totalAmount.addAndGet(amount); + count.incrementAndGet(); + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + final long now = clock.wallTime(); + final List ms = new ArrayList<>(2); + ms.add(new Measurement(countId, now, count.get())); + ms.add(new Measurement(totalAmountId, now, totalAmount.get())); + return ms; + } + + /** {@inheritDoc} */ + @Override + public long count() { + return count.get(); + } + + /** {@inheritDoc} */ + @Override + public long totalAmount() { + return totalAmount.get(); + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/DefaultId.java b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultId.java new file mode 100644 index 000000000..a80db6619 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultId.java @@ -0,0 +1,122 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import com.netflix.spectator.impl.Preconditions; + +import java.util.*; + +/** Id implementation for the default registry. */ +final class DefaultId implements Id { + + private static final Set EMPTY = new HashSet<>(); + + private final String name; + private final TagList tags; + + /** Create a new instance. */ + DefaultId(String name) { + this(name, TagList.EMPTY); + } + + /** Create a new instance. */ + DefaultId(String name, TagList tags) { + this.name = Preconditions.checkNotNull(name, "name"); + this.tags = tags; + } + + /** {@inheritDoc} */ + @Override + public String name() { + return name; + } + + /** {@inheritDoc} */ + @Override + public Iterable tags() { + return (tags == TagList.EMPTY) ? Collections.emptyList() : tags; + } + + /** {@inheritDoc} */ + @Override + public DefaultId withTag(Tag tag) { + return new DefaultId(name, new TagList(tag.key(), tag.value(), tags)); + } + + /** {@inheritDoc} */ + @Override + public DefaultId withTag(String key, String value) { + return new DefaultId(name, new TagList(key, value, tags)); + } + + /** + * Returns a new id with the tag list sorted by key and with no duplicate keys. If a duplicate + * is found the last entry in the list with a given key will be used. + */ + public DefaultId normalize() { + return rollup(EMPTY, false); + } + + /** + * Create a new id by removing tags from the list. This operation will 1) sort the list by the + * tag keys, 2) dedup entries if multiple have the same key, and 3) remove keys specified by + * the parameters. + * + * @param keys + * Set of keys to either keep or remove. + * @param keep + * If true, then the new id can only have tag keys in the provided set. Otherwise the new id + * can only have ids not in that set. + * @return + * New identifier after applying the rollup. + */ + public DefaultId rollup(Set keys, boolean keep) { + Map ts = new TreeMap<>(); + for (Tag t : tags) { + if (keys.contains(t.key()) == keep) { + ts.put(t.key(), t.value()); + } + } + return new DefaultId(name, TagList.create(ts)); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || !(obj instanceof DefaultId)) return false; + DefaultId other = (DefaultId) obj; + return name.equals(other.name) + && ((tags == null && other.tags == null) || (tags != null && tags.equals(other.tags))); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return name.hashCode() + (tags == null ? 0 : tags.hashCode()); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(name); + for (Tag t : tags()) { + buf.append(":").append(t.key()).append("=").append(t.value()); + } + return buf.toString(); + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/DefaultLongTaskTimer.java b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultLongTaskTimer.java new file mode 100644 index 000000000..fb4ccef21 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultLongTaskTimer.java @@ -0,0 +1,112 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** Long task timer implementation used by the extended registry. */ +final class DefaultLongTaskTimer implements LongTaskTimer { + + private static final double NANOS_PER_SECOND = (double) TimeUnit.SECONDS.toNanos(1L); + + private final Clock clock; + private final Id id; + private final ConcurrentMap tasks = new ConcurrentHashMap<>(); + private final AtomicLong nextTask = new AtomicLong(0L); + private final Id activeTasksId; + private final Id durationId; + + /** Create a new instance. */ + DefaultLongTaskTimer(Clock clock, Id id) { + this.clock = clock; + this.id = id; + + this.activeTasksId = id.withTag("statistic", "activeTasks"); + this.durationId = id.withTag("statistic", "duration"); + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public long start() { + long task = nextTask.getAndIncrement(); + tasks.put(task, clock.monotonicTime()); + return task; + } + + /** {@inheritDoc} */ + @Override + public long stop(long task) { + Long startTime = tasks.get(task); + if (startTime != null) { + tasks.remove(task); + return clock.monotonicTime() - startTime; + } else { + return -1L; + } + } + + /** {@inheritDoc} */ + @Override + public long duration(long task) { + Long startTime = tasks.get(task); + return (startTime != null) ? (clock.monotonicTime() - startTime) : -1L; + } + + /** {@inheritDoc} */ + @Override + public long duration() { + long now = clock.monotonicTime(); + long sum = 0L; + for (long startTime : tasks.values()) { + sum += now - startTime; + } + return sum; + } + + /** {@inheritDoc} */ + @Override + public int activeTasks() { + return tasks.size(); + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + final List ms = new ArrayList<>(2); + final long now = clock.wallTime(); + final double durationSeconds = duration() / NANOS_PER_SECOND; + ms.add(new Measurement(durationId, now, durationSeconds)); + ms.add(new Measurement(activeTasksId, now, activeTasks())); + return ms; + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/DefaultRegistry.java b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultRegistry.java new file mode 100644 index 000000000..e007fb8a8 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultRegistry.java @@ -0,0 +1,48 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** Default implementation of registry. */ +public final class DefaultRegistry extends AbstractRegistry { + + /** Create a new instance. */ + public DefaultRegistry() { + this(Clock.SYSTEM); + } + + /** Create a new instance. */ + public DefaultRegistry(Clock clock) { + super(clock); + } + + /** {@inheritDoc} */ + @Override + protected Counter newCounter(Id id) { + return new DefaultCounter(clock(), id); + } + + /** {@inheritDoc} */ + @Override + protected DistributionSummary newDistributionSummary(Id id) { + return new DefaultDistributionSummary(clock(), id); + } + + /** {@inheritDoc} */ + @Override + protected Timer newTimer(Id id) { + return new DefaultTimer(clock(), id); + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/DefaultTimer.java b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultTimer.java new file mode 100644 index 000000000..819f72bc4 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/DefaultTimer.java @@ -0,0 +1,110 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** Timer implementation for the default registry. */ +final class DefaultTimer implements Timer { + + private final Clock clock; + private final Id id; + private final AtomicLong count; + private final AtomicLong totalTime; + + private final Id countId; + private final Id totalTimeId; + + /** Create a new instance. */ + DefaultTimer(Clock clock, Id id) { + this.clock = clock; + this.id = id; + count = new AtomicLong(0L); + totalTime = new AtomicLong(0L); + countId = id.withTag("statistic", "count"); + totalTimeId = id.withTag("statistic", "totalTime"); + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void record(long amount, TimeUnit unit) { + final long nanos = TimeUnit.NANOSECONDS.convert(amount, unit); + totalTime.addAndGet(nanos); + count.incrementAndGet(); + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + final long now = clock.wallTime(); + final List ms = new ArrayList<>(2); + ms.add(new Measurement(countId, now, count.get())); + ms.add(new Measurement(totalTimeId, now, totalTime.get())); + return ms; + } + + /** {@inheritDoc} */ + @Override + public T record(Callable f) throws Exception { + final long s = clock.monotonicTime(); + try { + return f.call(); + } finally { + final long e = clock.monotonicTime(); + record(e - s, TimeUnit.NANOSECONDS); + } + } + + /** {@inheritDoc} */ + @Override + public void record(Runnable f) { + final long s = clock.monotonicTime(); + try { + f.run(); + } finally { + final long e = clock.monotonicTime(); + record(e - s, TimeUnit.NANOSECONDS); + } + } + + /** {@inheritDoc} */ + @Override + public long count() { + return count.get(); + } + + /** {@inheritDoc} */ + @Override + public long totalTime() { + return totalTime.get(); + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/DistributionSummary.java b/spectator-api/src/main/java/com/netflix/spectator/api/DistributionSummary.java new file mode 100644 index 000000000..6c3b4becc --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/DistributionSummary.java @@ -0,0 +1,43 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** + * Track the sample distribution of events. An example would be the response sizes for requests + * hitting and http server. + * + * The precise set of information maintained depends on the implementation. Most should try to + * provide a consistent implementation of {@link #count()} and {@link #totalAmount()}, + * but some implementations may not. In particular, the implementation from {@link NoopRegistry} + * will always return 0. + */ +public interface DistributionSummary extends Meter { + + /** + * Updates the statistics kept by the summary with the specified amount. + * + * @param amount + * Amount for an event being measured. For example, if the size in bytes of responses + * from a server. If the amount is less than 0 the value will be dropped. + */ + void record(long amount); + + /** The number of times that record has been called since this timer was created. */ + long count(); + + /** The total amount of all recorded events since this summary was created. */ + long totalAmount(); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/DoubleFunction.java b/spectator-api/src/main/java/com/netflix/spectator/api/DoubleFunction.java new file mode 100644 index 000000000..5dd1da0d6 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/DoubleFunction.java @@ -0,0 +1,40 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** + * Function to extract a double value from an object. + */ +public abstract class DoubleFunction implements ValueFunction { + + /** {@inheritDoc} */ + @Override + public double apply(Object obj) { + return (obj instanceof Number) + ? apply(((Number) obj).doubleValue()) + : Double.NaN; + } + + /** + * Apply a transform to the value `v`. + * + * @param v + * Double value to transform. + * @return + * Result of applying this function to `v`. + */ + public abstract double apply(double v); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/ExtendedRegistry.java b/spectator-api/src/main/java/com/netflix/spectator/api/ExtendedRegistry.java new file mode 100644 index 000000000..18f7e98d2 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/ExtendedRegistry.java @@ -0,0 +1,549 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import com.netflix.spectator.impl.Preconditions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + + +/** + * Wraps a registry and provides additional helper methods to make it easier to use. + */ +public final class ExtendedRegistry implements Registry { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExtendedRegistry.class); + + private final Registry impl; + + /** Create a new instance. */ + public ExtendedRegistry(Registry impl) { + this.impl = Preconditions.checkNotNull(impl, "impl"); + } + + /** Returns the underlying registry implementation that is being wrapped. */ + public Registry underlying() { + return impl; + } + + /** {@inheritDoc} */ + @Override + public Clock clock() { + return impl.clock(); + } + + /** {@inheritDoc} */ + @Override + public Id createId(String name) { + return impl.createId(name); + } + + /** {@inheritDoc} */ + @Override + public Id createId(String name, Iterable tags) { + return impl.createId(name, tags); + } + + /** {@inheritDoc} */ + @Override + public void register(Meter meter) { + impl.register(meter); + } + + /** {@inheritDoc} */ + @Override + public Counter counter(Id id) { + return impl.counter(id); + } + + /** {@inheritDoc} */ + @Override + public DistributionSummary distributionSummary(Id id) { + return impl.distributionSummary(id); + } + + /** {@inheritDoc} */ + @Override + public Timer timer(Id id) { + return impl.timer(id); + } + + /** {@inheritDoc} */ + @Override + public Meter get(Id id) { + return impl.get(id); + } + + /** {@inheritDoc} */ + @Override + public Iterator iterator() { + return impl.iterator(); + } + + /** {@inheritDoc} */ + @Override + public void addListener(RegistryListener listener) { + impl.addListener(listener); + } + + /** {@inheritDoc} */ + @Override + public void removeListener(RegistryListener listener) { + impl.removeListener(listener); + } + + ///////////////////////////////////////////////////////////////// + // Additional helper methods below + + private Iterable toIterable(String[] tags) { + if (tags.length % 2 == 1) { + throw new IllegalArgumentException("size must be even, it is a set of key=value pairs"); + } + TagList ts = TagList.EMPTY; + for (int i = 0; i < tags.length; i += 2) { + ts = new TagList(tags[i], tags[i + 1], ts); + } + return ts; + } + + /** + * Creates an identifier for a meter. + * + * @param name + * Description of the measurement that is being collected. + * @param tags + * Other dimensions that can be used to classify the measurement. + * @return + * Identifier for a meter. + */ + public Id createId(String name, String... tags) { + return impl.createId(name, toIterable(tags)); + } + + /** + * Measures the rate of some activity. + * + * @param name + * Description of the measurement that is being collected. + * @return + * Counter instance with the corresponding id. + */ + public Counter counter(String name) { + return impl.counter(impl.createId(name)); + } + + /** + * Measures the rate of some activity. + * + * @param name + * Description of the measurement that is being collected. + * @param tags + * Other dimensions that can be used to classify the measurement. + * @return + * Counter instance with the corresponding id. + */ + public Counter counter(String name, Iterable tags) { + return impl.counter(impl.createId(name, tags)); + } + + /** + * Measures the rate of some activity. + * + * @param name + * Description of the measurement that is being collected. + * @param tags + * Other dimensions that can be used to classify the measurement. + * @return + * Counter instance with the corresponding id. + */ + public Counter counter(String name, String... tags) { + return impl.counter(impl.createId(name, toIterable(tags))); + } + + /** + * Measures the sample distribution of events. + * + * @param name + * Description of the measurement that is being collected. + * @return + * Summary instance with the corresponding id. + */ + public DistributionSummary distributionSummary(String name) { + return impl.distributionSummary(impl.createId(name)); + } + + /** + * Measures the sample distribution of events. + * + * @param name + * Description of the measurement that is being collected. + * @param tags + * Other dimensions that can be used to classify the measurement. + * @return + * Summary instance with the corresponding id. + */ + public DistributionSummary distributionSummary(String name, Iterable tags) { + return impl.distributionSummary(impl.createId(name, tags)); + } + + /** + * Measures the sample distribution of events. + * + * @param name + * Description of the measurement that is being collected. + * @param tags + * Other dimensions that can be used to classify the measurement. + * @return + * Summary instance with the corresponding id. + */ + public DistributionSummary distributionSummary(String name, String... tags) { + return impl.distributionSummary(impl.createId(name, toIterable(tags))); + } + + /** + * Measures the time taken for short tasks. + * + * @param name + * Description of the measurement that is being collected. + * @return + * Timer instance with the corresponding id. + */ + public Timer timer(String name) { + return impl.timer(impl.createId(name)); + } + + /** + * Measures the time taken for short tasks. + * + * @param name + * Description of the measurement that is being collected. + * @param tags + * Other dimensions that can be used to classify the measurement. + * @return + * Timer instance with the corresponding id. + */ + public Timer timer(String name, Iterable tags) { + return impl.timer(impl.createId(name, tags)); + } + + /** + * Measures the time taken for short tasks. + * + * @param name + * Description of the measurement that is being collected. + * @param tags + * Other dimensions that can be used to classify the measurement. + * @return + * Timer instance with the corresponding id. + */ + public Timer timer(String name, String... tags) { + return impl.timer(impl.createId(name, toIterable(tags))); + } + + /** + * Measures the time taken for long tasks. + * + * @param id + * Identifier for the metric being registered. + * @return + * Timer instance with the corresponding id. + */ + public LongTaskTimer longTaskTimer(Id id) { + LongTaskTimer taskTimer = new DefaultLongTaskTimer(clock(), id); + impl.register(taskTimer); // the AggrMeter has the right semantics for these type of timers + return taskTimer; + } + + /** + * Measures the time taken for long tasks. + * + * @param name + * Description of the measurement that is being collected. + * @return + * Timer instance with the corresponding id. + */ + public LongTaskTimer longTaskTimer(String name) { + return longTaskTimer(createId(name)); + } + + /** + * Measures the time taken for long tasks. + * + * @param name + * Description of the measurement that is being collected. + * @param tags + * Other dimensions that can be used to classify the measurement. + * @return + * Timer instance with the corresponding id. + */ + public LongTaskTimer longTaskTimer(String name, Iterable tags) { + return longTaskTimer(createId(name, tags)); + } + + /** + * Measures the time taken for long tasks. + * + * @param name + * Description of the measurement that is being collected. + * @param tags + * Other dimensions that can be used to classify the measurement. + * @return + * Timer instance with the corresponding id. + */ + public LongTaskTimer longTaskTimer(String name, String... tags) { + return longTaskTimer(createId(name, toIterable(tags))); + } + + /** + * Register a gauge that reports the value of the {@link java.lang.Number}. The registration + * will keep a weak reference to the number so it will not prevent garbage collection. + * The number implementation used should be thread safe. + * + * @param id + * Identifier for the metric being registered. + * @param number + * Thread-safe implementation of {@link Number} used to access the value. + * @return + * The number that was passed in so the registration can be done as part of an assignment + * statement. + */ + public T gauge(Id id, T number) { + return gauge(id, number, Functions.IDENTITY); + } + + /** + * Register a gauge that reports the value of the {@link java.lang.Number}. See + * {@link #gauge(Id, Number)}. + * + * @param name + * Name of the metric being registered. + * @param number + * Thread-safe implementation of {@link Number} used to access the value. + * @return + * The number that was passed in so the registration can be done as part of an assignment + * statement. + */ + public T gauge(String name, T number) { + return gauge(impl.createId(name), number); + } + + /** + * Register a gauge that reports the value of the {@link java.lang.Number}. See + * {@link #gauge(Id, Number)}. + * + * @param name + * Name of the metric being registered. + * @param tags + * Sequence of dimensions for breaking down the name. + * @param number + * Thread-safe implementation of {@link Number} used to access the value. + * @return + * The number that was passed in so the registration can be done as part of an assignment + * statement. + */ + public T gauge(String name, Iterable tags, T number) { + return gauge(impl.createId(name, tags), number); + } + + /** + * Register a gauge that reports the value of the object after the function + * {@code f} is applied. The registration will keep a weak reference to the number so it will + * not prevent garbage collection. The number implementation used should be thread safe. + * + * @param id + * Identifier for the metric being registered. + * @param obj + * Object used to compute a value. + * @param f + * Function that is applied on the value for the number. + * @return + * The number that was passed in so the registration can be done as part of an assignment + * statement. + */ + public T gauge(Id id, T obj, ValueFunction f) { + impl.register(new ObjectGauge(clock(), id, obj, f)); + return obj; + } + + /** + * Register a gauge that reports the value of the object. See + * {@link #gauge(Id, Object, ValueFunction)}. + * + * @param name + * Name of the metric being registered. + * @param obj + * Object used to compute a value. + * @param f + * Function that is applied on the value for the number. + * @return + * The number that was passed in so the registration can be done as part of an assignment + * statement. + */ + public T gauge(String name, T obj, ValueFunction f) { + return gauge(impl.createId(name), obj, f); + } + + /** + * Register a gauge that reports the size of the {@link java.util.Collection}. The registration + * will keep a weak reference to the collection so it will not prevent garbage collection. + * The collection implementation used should be thread safe. Note that calling + * {@link java.util.Collection#size()} can be expensive for some collection implementations + * and should be considered before registering. + * + * @param id + * Identifier for the metric being registered. + * @param collection + * Thread-safe implementation of {@link Collection} used to access the value. + * @return + * The number that was passed in so the registration can be done as part of an assignment + * statement. + */ + public > T collectionSize(Id id, T collection) { + return gauge(id, collection, Functions.COLLECTION_SIZE); + } + + /** + * Register a gauge that reports the size of the {@link java.util.Collection}. The registration + * will keep a weak reference to the collection so it will not prevent garbage collection. + * The collection implementation used should be thread safe. Note that calling + * {@link java.util.Collection#size()} can be expensive for some collection implementations + * and should be considered before registering. + * + * @param name + * Name of the metric being registered. + * @param collection + * Thread-safe implementation of {@link Collection} used to access the value. + * @return + * The number that was passed in so the registration can be done as part of an assignment + * statement. + */ + public > T collectionSize(String name, T collection) { + return collectionSize(impl.createId(name), collection); + } + + /** + * Register a gauge that reports the size of the {@link java.util.Map}. The registration + * will keep a weak reference to the collection so it will not prevent garbage collection. + * The collection implementation used should be thread safe. Note that calling + * {@link java.util.Map#size()} can be expensive for some collection implementations + * and should be considered before registering. + * + * @param id + * Identifier for the metric being registered. + * @param collection + * Thread-safe implementation of {@link Map} used to access the value. + * @return + * The number that was passed in so the registration can be done as part of an assignment + * statement. + */ + public > T mapSize(Id id, T collection) { + return gauge(id, collection, Functions.MAP_SIZE); + } + + /** + * Register a gauge that reports the size of the {@link java.util.Map}. The registration + * will keep a weak reference to the collection so it will not prevent garbage collection. + * The collection implementation used should be thread safe. Note that calling + * {@link java.util.Map#size()} can be expensive for some collection implementations + * and should be considered before registering. + * + * @param name + * Name of the metric being registered. + * @param collection + * Thread-safe implementation of {@link Map} used to access the value. + * @return + * The number that was passed in so the registration can be done as part of an assignment + * statement. + */ + public > T mapSize(String name, T collection) { + return mapSize(impl.createId(name), collection); + } + + /** + * Register a gauge that reports the return value of invoking the method on the object. The + * registration will keep a weak reference to the object so it will not prevent garbage + * collection. The registered method should be thread safe and cheap to invoke. Any potentially + * long running or expensive activity such as IO should not be performed inline. + * + * @param id + * Identifier for the metric being registered. + * @param obj + * Object used to compute a value. + * @param method + * Name of the method to invoke on the object. + */ + public void methodValue(Id id, Object obj, String method) { + try { + final Method m = getMethod(obj.getClass(), method); + try { + // Make sure we can cast the response to a Number + final Number n = (Number) m.invoke(obj); + LOGGER.debug("registering gauge {}, using method [{}], with initial value {}", id, m, n); + gauge(id, obj, Functions.invokeMethod(m)); + } catch (Exception e) { + final String msg = "exception thrown invoking method [" + m + + "], skipping registration of gauge " + id; + Throwables.propagate(msg, e); + } + } catch (NoSuchMethodException e) { + final String mname = obj.getClass().getName() + "." + method; + final String msg = "invalid method [" + mname + "], skipping registration of gauge " + id; + Throwables.propagate(msg, e); + } + } + + /** + * Register a gauge that reports the return value of invoking the method on the object. The + * registration will keep a weak reference to the object so it will not prevent garbage + * collection. The registered method should be thread safe and cheap to invoke. Any potentially + * long running or expensive activity such as IO should not be performed inline. + * + * @param name + * Name of the metric being registered. + * @param obj + * Object used to compute a value. + * @param method + * Name of the method to invoke on the object. + */ + public void methodValue(String name, Object obj, String method) { + methodValue(impl.createId(name), obj, method); + } + + /** Search for a method in the class and all superclasses. */ + Method getMethod(Class cls, String name) throws NoSuchMethodException { + NoSuchMethodException firstExc = null; + for (Class c = cls; c != null; c = c.getSuperclass()) { + try { + return c.getDeclaredMethod(name); + } catch (NoSuchMethodException e) { + if (firstExc == null) { + firstExc = e; + } + } + } + throw firstExc; + } + + @Override + public String toString() { + return "ExtendedRegistry(impl=" + impl + ')'; + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Functions.java b/spectator-api/src/main/java/com/netflix/spectator/api/Functions.java new file mode 100644 index 000000000..7161732fa --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Functions.java @@ -0,0 +1,113 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + + +/** + * Common functions for use with gauges. + */ +public final class Functions { + + private static final Logger LOGGER = LoggerFactory.getLogger(Functions.class); + + private Functions() { + } + + /** + * Identity function that just returns the passed in value if it implements the + * {@link java.lang.Number} interface. + */ + public static final DoubleFunction IDENTITY = new DoubleFunction() { + public double apply(double v) { + return v; + } + }; + + /** + * Returns the size of the collection. + */ + public static final ValueFunction COLLECTION_SIZE = new ValueFunction() { + public double apply(Object obj) { + return (obj instanceof Collection) ? ((Collection) obj).size() : Double.NaN; + } + }; + + /** + * Returns the size of the map. + */ + public static final ValueFunction MAP_SIZE = new ValueFunction() { + public double apply(Object obj) { + return (obj instanceof Map) ? ((Map) obj).size() : Double.NaN; + } + }; + + /** + * Age function based on the system clock. See {@link #age(Clock)} for more details. + */ + public static final DoubleFunction AGE = age(Clock.SYSTEM); + + /** + * Returns a function that computes the age in seconds. The value passed into the function + * should be a timestamp in milliseconds since the epoch. Typically this will be the return + * value from calling {@link java.lang.System#currentTimeMillis()}. + * + * @param clock + * Clock used to get the current time for comparing with the passed in value. + * @return + * Function that computes the age. + */ + public static DoubleFunction age(final Clock clock) { + return new DoubleFunction() { + public double apply(double t) { + return (clock.wallTime() - t) / 1000.0; + } + }; + } + + /** + * Returns a function that invokes a method on the object via reflection. If an + * exception of any kind occurs then NaN will be returned and the exception will be logged. + * The method must have an empty parameter list and return a primitive number value or a + * subclass of {@link java.lang.Number}. The method will be set accessible so that private + * methods can be used. + * + * @param method + * Method to execute on the passed in object. + * @return + * Value returned by the method or NaN if an exception is thrown. + */ + public static ValueFunction invokeMethod(final Method method) { + method.setAccessible(true); + return new ValueFunction() { + public double apply(Object obj) { + try { + final Number n = (Number) method.invoke(obj); + return n.doubleValue(); + } catch (Exception e) { + LOGGER.warn("exception from method registered as a gauge [" + method + "]", e); + return Double.NaN; + } + } + }; + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Gauge.java b/spectator-api/src/main/java/com/netflix/spectator/api/Gauge.java new file mode 100644 index 000000000..69efe1263 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Gauge.java @@ -0,0 +1,25 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** + * A meter with a single value that can only be sampled at a point in time. A typical example is + * a queue size. + */ +public interface Gauge extends Meter { + /** Returns the current value. */ + double value(); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Id.java b/spectator-api/src/main/java/com/netflix/spectator/api/Id.java new file mode 100644 index 000000000..12a66452f --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Id.java @@ -0,0 +1,33 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** + * Identifier for a meter or measurement. + */ +public interface Id { + /** Description of the measurement that is being collected. */ + String name(); + + /** Other dimensions that can be used to classify the measurement. */ + Iterable tags(); + + /** New id with an additional tag value. */ + Id withTag(String k, String v); + + /** New id with an additional tag value. */ + Id withTag(Tag t); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/LongTaskTimer.java b/spectator-api/src/main/java/com/netflix/spectator/api/LongTaskTimer.java new file mode 100644 index 000000000..f5b8965dc --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/LongTaskTimer.java @@ -0,0 +1,55 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** + * Timer intended to track a small number of long running tasks. Example would be something like + * a batch hadoop job. Though "long running" is a bit subjective the assumption is that anything + * over a minute is long running. + */ +public interface LongTaskTimer extends Meter { + /** + * Start keeping time for a task and return a task id that can be used to look up how long + * it has been running. + */ + long start(); + + /** + * Indicates that a given task has completed. + * + * @param task + * Id for the task to stop. This should be the value returned from {@link #start()}. + * @return + * Duration for the task in nanoseconds. A -1 value will be returned for an unknown task. + */ + long stop(long task); + + /** + * Returns the current duration for a given active task. + * + * @param task + * Id for the task to stop. This should be the value returned from {@link #start()}. + * @return + * Duration for the task in nanoseconds. A -1 value will be returned for an unknown task. + */ + long duration(long task); + + /** Returns the cumulative duration of all current tasks in nanoseconds. */ + long duration(); + + /** Returns the current number of tasks being executed. */ + int activeTasks(); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/ManualClock.java b/spectator-api/src/main/java/com/netflix/spectator/api/ManualClock.java new file mode 100644 index 000000000..f74320b5d --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/ManualClock.java @@ -0,0 +1,68 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Clock implementation that allows the user to explicitly control the time. Typically used for + * unit tests. + */ +public class ManualClock implements Clock { + + private final AtomicLong wall; + private final AtomicLong monotonic; + + /** Create a new instance. */ + public ManualClock() { + this(0L, 0L); + } + + /** + * Create a new instance. + * + * @param wallInit + * Initial value for the wall time. + * @param monotonicInit + * Initial value for the monotonic time. + */ + public ManualClock(long wallInit, long monotonicInit) { + wall = new AtomicLong(wallInit); + monotonic = new AtomicLong(monotonicInit); + } + + /** {@inheritDoc} */ + @Override + public long wallTime() { + return wall.get(); + } + + /** {@inheritDoc} */ + @Override + public long monotonicTime() { + return monotonic.get(); + } + + /** Set the wall time to the value {@code t}. */ + public void setWallTime(long t) { + wall.set(t); + } + + /** Set the monotonic time to the value {@code t}. */ + public void setMonotonicTime(long t) { + monotonic.set(t); + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Measurement.java b/spectator-api/src/main/java/com/netflix/spectator/api/Measurement.java new file mode 100644 index 000000000..fcec7ec2f --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Measurement.java @@ -0,0 +1,75 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** + * A measurement sampled from a meter. + */ +public final class Measurement { + + private final Id id; + private final long timestamp; + private final double value; + + /** Create a new instance. */ + public Measurement(Id id, long timestamp, double value) { + this.id = id; + this.timestamp = timestamp; + this.value = value; + } + + /** Identifier for the measurement. */ + public Id id() { + return id; + } + + /** + * The timestamp in milliseconds since the epoch for when the measurement was taken. + */ + public long timestamp() { + return timestamp; + } + + /** Value for the measurement. */ + public double value() { + return value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || !(obj instanceof Measurement)) return false; + Measurement other = (Measurement) obj; + return id.equals(other.id) + && timestamp == other.timestamp + && Double.compare(value, other.value) == 0; + } + + @Override + public int hashCode() { + final int prime = 31; + int hc = prime; + hc = prime * hc + id.hashCode(); + hc = prime * hc + Long.valueOf(timestamp).hashCode(); + hc = prime * hc + Double.valueOf(value).hashCode(); + return hc; + } + + @Override + public String toString() { + return "Measurement(" + id.toString() + "," + timestamp + "," + value + ")"; + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Meter.java b/spectator-api/src/main/java/com/netflix/spectator/api/Meter.java new file mode 100644 index 000000000..fa8c6d744 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Meter.java @@ -0,0 +1,39 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** + * A device for collecting a set of measurements. Note, this interface is only intended to be + * implemented by registry implementations. + */ +public interface Meter { + + /** + * Identifier used to lookup this meter in the registry. + */ + Id id(); + + /** + * Get the set of measurements for this meter. + */ + Iterable measure(); + + /** + * Indicates whether the meter is expired. For example, a counter might expire if there is no + * activity within a given time frame. + */ + boolean hasExpired(); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/NoopCounter.java b/spectator-api/src/main/java/com/netflix/spectator/api/NoopCounter.java new file mode 100644 index 000000000..345d87af0 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/NoopCounter.java @@ -0,0 +1,58 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.Collections; + +/** Counter implementation for the no-op registry. */ +enum NoopCounter implements Counter { + /** Singleton instance. */ + INSTANCE; + + /** {@inheritDoc} */ + @Override + public Id id() { + return NoopId.INSTANCE; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void increment() { + } + + /** {@inheritDoc} */ + @Override + public void increment(long amount) { + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + return Collections.emptyList(); + } + + /** {@inheritDoc} */ + @Override + public long count() { + return 0L; + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/NoopDistributionSummary.java b/spectator-api/src/main/java/com/netflix/spectator/api/NoopDistributionSummary.java new file mode 100644 index 000000000..ff3711f76 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/NoopDistributionSummary.java @@ -0,0 +1,60 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.Collections; + +/** Distribution summary implementation for the no-op registry. */ +enum NoopDistributionSummary implements DistributionSummary { + + /** Singleton instance. */ + INSTANCE; + + /** {@inheritDoc} */ + @Override + public Id id() { + return NoopId.INSTANCE; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void record(long amount) { + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + return Collections.emptyList(); + } + + /** {@inheritDoc} */ + @Override + public long count() { + return 0L; + } + + /** {@inheritDoc} */ + @Override + public long totalAmount() { + return 0L; + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/NoopId.java b/spectator-api/src/main/java/com/netflix/spectator/api/NoopId.java new file mode 100644 index 000000000..d10544a86 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/NoopId.java @@ -0,0 +1,57 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.Collections; + +/** Id implementation for the no-op registry. */ +final class NoopId implements Id { + /** Singleton instance. */ + static final Id INSTANCE = new NoopId(); + + private NoopId() { + } + + /** {@inheritDoc} */ + @Override + public String name() { + return "noop"; + } + + /** {@inheritDoc} */ + @Override + public Iterable tags() { + return Collections.emptyList(); + } + + /** {@inheritDoc} */ + @Override + public Id withTag(String k, String v) { + return this; + } + + /** {@inheritDoc} */ + @Override + public Id withTag(Tag tag) { + return this; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return name(); + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/NoopRegistry.java b/spectator-api/src/main/java/com/netflix/spectator/api/NoopRegistry.java new file mode 100644 index 000000000..5df62851f --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/NoopRegistry.java @@ -0,0 +1,90 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.Collections; +import java.util.Iterator; + +/** + * Registry implementation that does nothing. This is typically used to allow for performance tests + * to see how much overhead is being added by instrumentation. This implementation tries to do the + * minimum amount possible without requiring code changes for users. + */ +public final class NoopRegistry implements Registry { + + /** {@inheritDoc} */ + @Override + public Clock clock() { + return Clock.SYSTEM; + } + + /** {@inheritDoc} */ + @Override + public Id createId(String name) { + return NoopId.INSTANCE; + } + + /** {@inheritDoc} */ + @Override + public Id createId(String name, Iterable tags) { + return NoopId.INSTANCE; + } + + /** {@inheritDoc} */ + @Override + public void register(Meter meter) { + } + + /** {@inheritDoc} */ + @Override + public Counter counter(Id id) { + return NoopCounter.INSTANCE; + } + + /** {@inheritDoc} */ + @Override + public DistributionSummary distributionSummary(Id id) { + return NoopDistributionSummary.INSTANCE; + } + + /** {@inheritDoc} */ + @Override + public Timer timer(Id id) { + return NoopTimer.INSTANCE; + } + + /** {@inheritDoc} */ + @Override + public Meter get(Id id) { + return null; + } + + /** {@inheritDoc} */ + @Override + public Iterator iterator() { + return Collections.emptyList().iterator(); + } + + /** {@inheritDoc} */ + @Override + public void addListener(RegistryListener listener) { + } + + /** {@inheritDoc} */ + @Override + public void removeListener(RegistryListener listener) { + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/NoopTimer.java b/spectator-api/src/main/java/com/netflix/spectator/api/NoopTimer.java new file mode 100644 index 000000000..d75bf1ea1 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/NoopTimer.java @@ -0,0 +1,73 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.Collections; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +/** Counter implementation for the no-op registry. */ +enum NoopTimer implements Timer { + /** Singleton instance. */ + INSTANCE; + + /** {@inheritDoc} */ + @Override + public Id id() { + return NoopId.INSTANCE; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void record(long amount, TimeUnit unit) { + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + return Collections.emptyList(); + } + + /** {@inheritDoc} */ + @Override + public T record(Callable f) throws Exception { + return f.call(); + } + + /** {@inheritDoc} */ + @Override + public void record(Runnable f) { + f.run(); + } + + /** {@inheritDoc} */ + @Override + public long count() { + return 0L; + } + + /** {@inheritDoc} */ + @Override + public long totalTime() { + return 0L; + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/ObjectGauge.java b/spectator-api/src/main/java/com/netflix/spectator/api/ObjectGauge.java new file mode 100644 index 000000000..7d2e4418a --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/ObjectGauge.java @@ -0,0 +1,57 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.Collections; + +/** + * Gauge that is defined by executing a {@link ValueFunction} on an object. + */ +class ObjectGauge extends AbstractMeter implements Gauge { + + private final ValueFunction f; + + /** + * Create a gauge that samples the provided number for the value. + * + * @param clock + * Clock used for accessing the current time. + * @param id + * Identifier for the gauge. + * @param obj + * {@link Object} used to access the value. + * @param f + * Function that is applied on the value for the number. The operation {@code f.apply(obj)} + * should be thread-safe. + */ + ObjectGauge(Clock clock, Id id, Object obj, ValueFunction f) { + super(clock, id, obj); + this.f = f; + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + return Collections.singleton(new Measurement(id, clock.wallTime(), value())); + } + + /** {@inheritDoc} */ + @Override + public double value() { + final Object obj = ref.get(); + return (obj == null) ? Double.NaN : f.apply(obj); + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Registry.java b/spectator-api/src/main/java/com/netflix/spectator/api/Registry.java new file mode 100644 index 000000000..eec408768 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Registry.java @@ -0,0 +1,99 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.Iterator; + +/** + * Registry to manage a set of meters. + */ +public interface Registry extends Iterable { + + /** + * The clock used by the registry for timing events. + */ + Clock clock(); + + /** + * Creates an identifier for a meter. All ids passed into other calls should be created by the + * registry. + * + * @param name + * Description of the measurement that is being collected. + */ + Id createId(String name); + + /** + * Creates an identifier for a meter. All ids passed into other calls should be created by the + * registry. + * + * @param name + * Description of the measurement that is being collected. + * @param tags + * Other dimensions that can be used to classify the measurement. + */ + Id createId(String name, Iterable tags); + + /** + * Add a custom meter to the registry. + */ + void register(Meter meter); + + /** + * Measures the rate of some activity. A counter is for continuously incrementing sources like + * the number of requests that are coming into a server. + * + * @param id + * Identifier created by a call to {@link #createId} + */ + Counter counter(Id id); + + /** + * Measures the rate and variation in amount for some activity. For example, it could be used to + * get insight into the variation in response sizes for requests to a server. + * + * @param id + * Identifier created by a call to {@link #createId} + */ + DistributionSummary distributionSummary(Id id); + + /** + * Measures the rate and time taken for short running tasks. + * + * @param id + * Identifier created by a call to {@link #createId} + */ + Timer timer(Id id); + + /** + * Returns the meter associated with a given id. + * + * @param id + * Identifier for the meter. + * @return + * Instance of the meter or null if there is no match. + */ + Meter get(Id id); + + /** Iterator for traversing the set of meters in the registry. */ + Iterator iterator(); + + /** Add a listener that will get notified whenever a meter is added into the registry. */ + void addListener(RegistryListener listener); + + /** Remove a listener from the registry. */ + void removeListener(RegistryListener listener); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/RegistryListener.java b/spectator-api/src/main/java/com/netflix/spectator/api/RegistryListener.java new file mode 100644 index 000000000..b7b00de53 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/RegistryListener.java @@ -0,0 +1,25 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + + +/** + * Callback interface to receive notifications when meters are added to the registry. + */ +public interface RegistryListener { + /** Invoked when a meter is added to the registry. */ + void onAdd(Meter meter); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Spectator.java b/spectator-api/src/main/java/com/netflix/spectator/api/Spectator.java new file mode 100644 index 000000000..2cc0f4f6b --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Spectator.java @@ -0,0 +1,91 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; +import java.util.ServiceLoader; + +/** + * Static factory used to access the main global registry. The registry class to use can be + * set with the system property spectator.api.registryClass. + */ +public final class Spectator { + + private static final Logger LOGGER = LoggerFactory.getLogger(Spectator.class); + + private static final ExtendedRegistry REGISTRY = + new ExtendedRegistry(newInstance(Config.registryClass())); + + /** + * Create a new registry instance using {@link java.util.ServiceLoader}. If no implementations + * are found the default will be used. + */ + static Registry newInstanceUsingServiceLoader() { + final ServiceLoader loader = ServiceLoader.load(Registry.class); + final Iterator registryIterator = loader.iterator(); + if (registryIterator.hasNext()) { + Registry r = registryIterator.next(); + LOGGER.info("using first registry impl found in classpath: {}", r.getClass().getName()); + return r; + } else { + LOGGER.warn("no registry impl found in classpath, using default"); + return new DefaultRegistry(); + } + } + + /** + * Create a new registry instance using the class name specified by the system property + * {@code spectator.api.registryClass}. If no implementations are found the default will be used. + */ + static Registry newInstanceUsingClassName(String name) { + try { + final Class c = Class.forName(name); + return (Registry) c.newInstance(); + } catch (Exception e) { + final String msg = "failed to instantiate registry class '" + name + + "', falling back to default implementation"; + Throwables.propagate(new RuntimeException(msg, e)); + return new DefaultRegistry(); + } + } + + /** Create a new instance of the registry. */ + static Registry newInstance(String name) { + return Config.SERVICE_LOADER.equals(name) + ? newInstanceUsingServiceLoader() + : newInstanceUsingClassName(name); + } + + /** + * Return the default global registry implementation. The implementation used will depend on the + * system property {@code spectator.api.registryClass}. If not set or set to + * {@code service-loader} the registry class will be determined by scanning the classpath using + * {@link java.util.ServiceLoader}. Otherwise an instance of the classname specified will be + * used. If a registry cannot be loaded the fallback is to use the {@link DefaultRegistry}. + * When {@code spectator.api.propagateWarnings} is set to {@code true} and an explicit class name + * is specified a {@link java.lang.RuntimeException} will be thrown if the specified class cannot + * be used. + */ + public static ExtendedRegistry registry() { + return REGISTRY; + } + + private Spectator() { + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Tag.java b/spectator-api/src/main/java/com/netflix/spectator/api/Tag.java new file mode 100644 index 000000000..60fa61b07 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Tag.java @@ -0,0 +1,25 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** Key/value pair used to classify and drill into measurements. */ +public interface Tag { + /** Key for the tag. */ + String key(); + + /** Value for the tag. */ + String value(); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/TagList.java b/spectator-api/src/main/java/com/netflix/spectator/api/TagList.java new file mode 100644 index 000000000..265358ff9 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/TagList.java @@ -0,0 +1,151 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import com.netflix.spectator.impl.Preconditions; + +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; + +/** + * A tag list implemented as a singly linked list. Doesn't automatically dedup keys but supports + * a cheap prepend at the call site to allow for inexpensive dynamic ids. + */ +final class TagList implements Iterable, Tag { + + private final String key; + private final String value; + private final TagList next; + private final int hc; + + /** + * Create a new instance with a single pair in the list. + */ + TagList(String key, String value) { + this(key, value, EMPTY); + } + + /** + * Create a new instance with a new tag prepended to the list {@code next}. + */ + TagList(String key, String value, TagList next) { + this.key = Preconditions.checkNotNull(key, "key"); + this.value = Preconditions.checkNotNull(value, "value"); + this.next = next; + this.hc = 31 * (key.hashCode() + value.hashCode() + (next == null ? 23 : next.hashCode())); + } + + /** {@inheritDoc} */ + @Override + public String key() { + return key; + } + + /** {@inheritDoc} */ + @Override + public String value() { + return value; + } + + /** {@inheritDoc} */ + @Override + public Iterator iterator() { + final TagList root = this; + return new Iterator() { + private TagList current = root; + + public boolean hasNext() { + return current != null; + } + + public Tag next() { + if (current == null) { + throw new NoSuchElementException(); + } + Tag tmp = current; + current = current.next; + return tmp; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || !(obj instanceof TagList)) return false; + TagList other = (TagList) obj; + return key.equals(other.key) && value.equals(other.value) + && (next == other.next || (next != null && next.equals(other.next))); + } + + /** + * This object is immutable and the hash code is precomputed in the constructor. The id object + * is typically created to lookup a Meter based on dynamic dimensions so we assume that it is + * highly likely that the hash code method will be called and that it could be in a fairly + * high volume execution path. + * + * {@inheritDoc} + */ + @Override + public int hashCode() { + return hc; + } + + /** + * Create a new tag list from the key/value pairs in the iterable. + * + * @param tags + * Set of key/value pairs. + * @return + * New tag list with a copy of the data. + */ + public static TagList create(Iterable tags) { + if (tags instanceof TagList) { + return (TagList) tags; + } else { + TagList head = null; + for (Tag t : tags) { + head = new TagList(t.key(), t.value(), head); + } + return head; + } + } + + /** + * Create a new tag list from the key/value pairs in the map. + * + * @param tags + * Set of key/value pairs. + * @return + * New tag list with a copy of the data. + */ + public static TagList create(Map tags) { + TagList head = null; + for (Map.Entry t : tags.entrySet()) { + head = new TagList(t.getKey(), t.getValue(), head); + } + return head; + } + + /** Empty tag list. */ + static final TagList EMPTY = null; +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Throwables.java b/spectator-api/src/main/java/com/netflix/spectator/api/Throwables.java new file mode 100644 index 000000000..33db50adc --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Throwables.java @@ -0,0 +1,59 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** Helper functions for working with exceptions. */ +final class Throwables { + + private static final Logger LOGGER = LoggerFactory.getLogger(Throwables.class); + + /** + * Log a warning using the message from the exception and if enabled propagate the + * exception {@code t}. + * + * @param t + * Exception to log and optionally propagate. + */ + static void propagate(Throwable t) { + propagate(t.getMessage(), t); + } + + /** + * Log a warning and if enabled propagate the exception {@code t}. The propagation is controlled + * by the system property {@code spectator.api.propagateWarnings}. + * + * @param msg + * Message written out to the log. + * @param t + * Exception to log and optionally propagate. + */ + static void propagate(String msg, Throwable t) { + LOGGER.warn(msg, t); + if (Config.propagateWarnings()) { + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } else { + throw new RuntimeException(t); + } + } + } + + private Throwables() { + } +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/Timer.java b/spectator-api/src/main/java/com/netflix/spectator/api/Timer.java new file mode 100644 index 000000000..6bad272a2 --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/Timer.java @@ -0,0 +1,66 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +/** + * Timer intended to track a large number of short running events. Example would be something like + * an http request. Though "short running" is a bit subjective the assumption is that it should be + * under a minute. + * + * The precise set of information maintained by the timer depends on the implementation. Most + * should try to provide a consistent implementation of {@link #count()} and {@link #totalTime()}, + * but some implementations may not. In particular, the implementation from {@link NoopRegistry} + * will always return 0. + */ +public interface Timer extends Meter { + /** + * Updates the statistics kept by the counter with the specified amount. + * + * @param amount + * Duration of a single event being measured by this timer. If the amount is less than 0 + * the value will be dropped. + * @param unit + * Time unit for the amount being recorded. + */ + void record(long amount, TimeUnit unit); + + /** + * Executes the callable `f` and records the time taken. + * + * @param f + * Function to execute and measure the execution time. + * @return + * The return value of `f`. + */ + T record(Callable f) throws Exception; + + /** + * Executes the runnable `f` and records the time taken. + * + * @param f + * Function to execute and measure the execution time. + */ + void record(Runnable f); + + /** The number of times that record has been called since this timer was created. */ + long count(); + + /** The total time in nanoseconds of all recorded events since this timer was created. */ + long totalTime(); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/ValueFunction.java b/spectator-api/src/main/java/com/netflix/spectator/api/ValueFunction.java new file mode 100644 index 000000000..25caad0ab --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/ValueFunction.java @@ -0,0 +1,31 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +/** + * Function to extract a double value from an object. + */ +public interface ValueFunction { + /** + * Returns a double value based on the object {@code ref}. + * + * @param ref + * An object to use for extracting the value. + * @return + * Double value based on the object. + */ + double apply(Object ref); +} diff --git a/spectator-api/src/main/java/com/netflix/spectator/api/package-info.java b/spectator-api/src/main/java/com/netflix/spectator/api/package-info.java new file mode 100644 index 000000000..e413c364d --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/api/package-info.java @@ -0,0 +1,83 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Primary interfaces for working with spectator. To get started, here is a small code sample: + * + *
+ * Server s = new Server(Spectator.registry());
+ *
+ * class Server {
+ *   private final ExtendedRegistry registry;
+ *   private final Id requestCountId;
+ *   private final Timer requestLatency;
+ *   private final DistributionSummary responseSizes;
+ *
+ *   public Server(ExtendedRegistry registry) {
+ *     this.registry = registry;
+ *     requestCountId = registry.createId("server.requestCount");
+ *     requestLatency = registry.timer("server.requestLatency");
+ *     responseSizes = registry.distributionSummary("server.responseSizes");
+ *     registry.methodValue("server.numConnections", this, "getNumConnections");
+ *   }
+ *
+ *   public Response handle(Request req) {
+ *     final long s = System.nanoTime();
+ *     try {
+ *       Response res = doSomething(req);
+ *
+ *       final Id cntId = requestCountId
+ *         .withTag("country", req.country())
+ *         .withTag("status", res.status());
+ *       registry.counter(cntId).increment();
+ *
+ *       responseSizes.record(res.body().size());
+ *
+ *       return res;
+ *     } catch (Exception e) {
+ *       final Id cntId = requestCountId
+ *         .withTag("country", req.country())
+ *         .withTag("status", "exception")
+ *         .withTag("error", e.getClass().getSimpleName());
+ *       registry.counter(cntId).increment();
+ *       throw e;
+ *     } finally {
+ *       requestLatency.record(System.nanoTime() - s, TimeUnit.NANOSECONDS);
+ *     }
+ *   }
+ *
+ *   public int getNumConnections() {
+ *     // however we determine the current number of connections on the server
+ *   }
+ * }
+ * 
+ * + * The main classes you will need to understand: + * + *
    + *
  • {@link com.netflix.spectator.api.Spectator}: static entrypoint to access the registry.
  • + *
  • {@link com.netflix.spectator.api.ExtendedRegistry}: registry class used to create + * meters.
  • + *
  • {@link com.netflix.spectator.api.Counter}: meter type for measuring a rate of change.
  • + *
  • {@link com.netflix.spectator.api.Timer}: meter type for measuring the time for many short + * events.
  • + *
  • {@link com.netflix.spectator.api.LongTaskTimer}: meter type for measuring the time for a + * few long events.
  • + *
  • {@link com.netflix.spectator.api.DistributionSummary}: meter type for measuring the sample + * distribution of some type of events.
  • + *
+ */ +package com.netflix.spectator.api; diff --git a/spectator-api/src/main/java/com/netflix/spectator/impl/Preconditions.java b/spectator-api/src/main/java/com/netflix/spectator/impl/Preconditions.java new file mode 100644 index 000000000..4d46a892b --- /dev/null +++ b/spectator-api/src/main/java/com/netflix/spectator/impl/Preconditions.java @@ -0,0 +1,46 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.impl; + +/** + * Internal convenience methods that help a method or constructor check whether it was invoked + * correctly. Please notice that this should be considered an internal implementation detail, and + * it is subject to change without notice. + */ +public final class Preconditions { + private Preconditions() { + } + + /** + * Ensures the object reference is not null. + */ + public static T checkNotNull(T obj, String name) { + if (obj == null) { + String msg = String.format("parameter '%s' cannot be null", name); + throw new NullPointerException(msg); + } + return obj; + } + + /** + * Ensures the truth of an expression involving the state of the calling instance. + */ + public static void checkState(boolean expression, String errMsg) { + if (!expression) { + throw new IllegalStateException(errMsg); + } + } +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/DefaultCounterTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultCounterTest.java new file mode 100644 index 000000000..b7344702a --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultCounterTest.java @@ -0,0 +1,63 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class DefaultCounterTest { + + private final ManualClock clock = new ManualClock(); + + @Test + public void testInit() { + Counter c = new DefaultCounter(clock, NoopId.INSTANCE); + Assert.assertEquals(c.count(), 0L); + } + + @Test + public void testIncrement() { + Counter c = new DefaultCounter(clock, NoopId.INSTANCE); + c.increment(); + Assert.assertEquals(c.count(), 1L); + c.increment(); + c.increment(); + Assert.assertEquals(c.count(), 3L); + } + + @Test + public void testIncrementAmount() { + Counter c = new DefaultCounter(clock, NoopId.INSTANCE); + c.increment(42); + Assert.assertEquals(c.count(), 42L); + } + + @Test + public void testMeasure() { + Counter c = new DefaultCounter(clock, NoopId.INSTANCE); + c.increment(42); + clock.setWallTime(3712345L); + for (Measurement m : c.measure()) { + Assert.assertEquals(m.id(), c.id()); + Assert.assertEquals(m.timestamp(), 3712345L); + Assert.assertEquals(m.value(), 42.0, 0.1e-12); + } + } + +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/DefaultDistributionSummaryTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultDistributionSummaryTest.java new file mode 100644 index 000000000..fa61feeb0 --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultDistributionSummaryTest.java @@ -0,0 +1,60 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class DefaultDistributionSummaryTest { + + private final ManualClock clock = new ManualClock(); + + @Test + public void testInit() { + DistributionSummary t = new DefaultDistributionSummary(clock, NoopId.INSTANCE); + Assert.assertEquals(t.count(), 0L); + Assert.assertEquals(t.totalAmount(), 0L); + } + + @Test + public void testRecord() { + DistributionSummary t = new DefaultDistributionSummary(clock, NoopId.INSTANCE); + t.record(42); + Assert.assertEquals(t.count(), 1L); + Assert.assertEquals(t.totalAmount(), 42L); + } + + @Test + public void testMeasure() { + DistributionSummary t = new DefaultDistributionSummary(clock, new DefaultId("foo")); + t.record(42); + clock.setWallTime(3712345L); + for (Measurement m : t.measure()) { + Assert.assertEquals(m.timestamp(), 3712345L); + if (m.id().equals(t.id().withTag("statistic", "count"))) { + Assert.assertEquals(m.value(), 1.0, 0.1e-12); + } else if (m.id().equals(t.id().withTag("statistic", "totalAmount"))) { + Assert.assertEquals(m.value(), 42.0, 0.1e-12); + } else { + Assert.fail("unexpected id: " + m.id()); + } + } + } + +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/DefaultIdTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultIdTest.java new file mode 100644 index 000000000..2daa8098f --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultIdTest.java @@ -0,0 +1,98 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import nl.jqno.equalsverifier.EqualsVerifier; +import nl.jqno.equalsverifier.Warning; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.HashSet; +import java.util.Set; + +@RunWith(JUnit4.class) +public class DefaultIdTest { + + @Test(expected = NullPointerException.class) + public void testNullName() { + new DefaultId(null); + } + + @Test + public void testName() { + Id id = new DefaultId("foo"); + Assert.assertEquals(id.name(), "foo"); + } + + @Test + public void testTags() { + TagList ts = new TagList("k1", "v1"); + Id id = new DefaultId("foo", ts); + Assert.assertEquals(id.name(), "foo"); + Assert.assertEquals(id.tags(), ts); + } + + @Test + public void testTagsEmpty() { + Id id = new DefaultId("foo"); + Assert.assertTrue(!id.tags().iterator().hasNext()); + } + + @Test + public void equalsContractTest() { + TagList ts1 = new TagList("k1", "v1"); + TagList ts2 = new TagList("k2", "v2", ts1); + EqualsVerifier + .forClass(DefaultId.class) + .withPrefabValues(TagList.class, ts1, ts2) + .suppress(Warning.NULL_FIELDS) + .verify(); + } + + @Test + public void testNormalize() { + DefaultId id12 = (new DefaultId("foo")).withTag("k1", "v1").withTag("k2", "v2"); + DefaultId id21 = (new DefaultId("foo")).withTag("k2", "v2").withTag("k1", "v1"); + Assert.assertTrue(!id12.equals(id21)); + Assert.assertEquals(id12, id21.normalize()); + } + + @Test + public void testRollup() { + Set keys = new HashSet<>(); + keys.add("k1"); + keys.add("foo"); + DefaultId id = (new DefaultId("foo")).withTag("k1", "v1").withTag("k2", "v2"); + DefaultId keepId = (new DefaultId("foo")).withTag("k1", "v1"); + DefaultId dropId = (new DefaultId("foo")).withTag("k2", "v2"); + Assert.assertEquals(keepId, id.rollup(keys, true)); + Assert.assertEquals(dropId, id.rollup(keys, false)); + } + + @Test + public void testToString() { + DefaultId id = (new DefaultId("foo")).withTag("k1", "v1").withTag("k2", "v2"); + Assert.assertEquals(id.toString(), "foo:k2=v2:k1=v1"); + } + + @Test + public void testToStringNameOnly() { + DefaultId id = new DefaultId("foo"); + Assert.assertEquals(id.toString(), "foo"); + } +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/DefaultLongTaskTimerTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultLongTaskTimerTest.java new file mode 100644 index 000000000..897003438 --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultLongTaskTimerTest.java @@ -0,0 +1,95 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class DefaultLongTaskTimerTest { + private final ManualClock clock = new ManualClock(); + + @Test + public void testInit() { + LongTaskTimer t = new DefaultLongTaskTimer(clock, NoopId.INSTANCE); + Assert.assertEquals(t.duration(), 0L); + Assert.assertEquals(t.activeTasks(), 0L); + } + + + @Test + public void testStart() { + LongTaskTimer t = new DefaultLongTaskTimer(clock, NoopId.INSTANCE); + + long task1 = t.start(); + long task2 = t.start(); + + Assert.assertFalse(task1 == task2); + Assert.assertEquals(t.activeTasks(), 2); + Assert.assertEquals(t.duration(), 0L); + } + + @Test + public void testStop() { + LongTaskTimer t = new DefaultLongTaskTimer(clock, NoopId.INSTANCE); + + long task1 = t.start(); + long task2 = t.start(); + + Assert.assertEquals(t.activeTasks(), 2); + clock.setMonotonicTime(5L); + Assert.assertEquals(t.duration(), 10L); + + long elapsed1 = t.stop(task1); + Assert.assertEquals(elapsed1, 5L); + Assert.assertEquals(t.duration(task2), 5L); + Assert.assertEquals(t.duration(task1), -1L); // task is gone, should return default + Assert.assertEquals(t.duration(), 5L); + } + + static void assertLongTaskTimer(Meter t, long timestamp, int activeTasks, double duration) { + for (Measurement m : t.measure()) { + Assert.assertEquals(m.timestamp(), timestamp); + if (m.id().equals(t.id().withTag("statistic", "activeTasks"))) { + Assert.assertEquals(m.value(), activeTasks, 1.0e-12); + } else if (m.id().equals(t.id().withTag("statistic", "duration"))) { + Assert.assertEquals(m.value(), duration, 1.0e-12); + } else { + Assert.fail("unexpected id: " + m.id()); + } + } + } + + @Test + public void testMeasure() { + LongTaskTimer t = new DefaultLongTaskTimer(clock, new DefaultId("foo")); + long task1 = t.start(); + clock.setMonotonicTime(1_000_000_000L); + clock.setWallTime(1L); + assertLongTaskTimer(t, 1L, 1, 1.0); + + long task2 = t.start(); + assertLongTaskTimer(t, 1L, 2, 1.0); + + t.stop(task1); + assertLongTaskTimer(t, 1L, 1, 0.0); + + t.stop(task2); + assertLongTaskTimer(t, 1L, 0, 0.0); + } +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/DefaultRegistryTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultRegistryTest.java new file mode 100644 index 000000000..b9a20f7e0 --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultRegistryTest.java @@ -0,0 +1,267 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +@RunWith(JUnit4.class) +public class DefaultRegistryTest { + + private final ManualClock clock = new ManualClock(); + + @Before + public void init() { + System.setProperty("spectator.api.propagateWarnings", "true"); + System.setProperty("spectator.api.maxNumberOfMeters", "10000"); + } + + @Test + public void testCreateId() { + Registry r = new DefaultRegistry(clock); + Assert.assertEquals(r.createId("foo"), new DefaultId("foo")); + } + + @Test + public void testCreateIdWithTags() { + Registry r = new DefaultRegistry(clock); + TagList ts = new TagList("k", "v"); + Assert.assertEquals(r.createId("foo", ts), new DefaultId("foo", ts)); + } + + @Test + public void testRegister() { + Registry r = new DefaultRegistry(clock); + Counter c = new DefaultCounter(clock, r.createId("foo")); + r.register(c); + c.increment(); + Assert.assertEquals(c.count(), 1L); + r.register(c); + Meter meter = r.get(c.id()); + for (Measurement m : meter.measure()) { + Assert.assertEquals(m.value(), 2.0, 1e-12); + } + } + + @Test + public void testCounter() { + Registry r = new DefaultRegistry(clock); + Counter c = r.counter(r.createId("foo")); + c.increment(); + Assert.assertEquals(c.count(), 1L); + + Counter c2 = r.counter(r.createId("foo")); + Assert.assertSame(c, c2); + } + + @Test + public void testTimer() { + Registry r = new DefaultRegistry(clock); + Timer t = r.timer(r.createId("foo")); + t.record(42L, TimeUnit.MILLISECONDS); + Assert.assertEquals(t.count(), 1L); + + Timer t2 = r.timer(r.createId("foo")); + Assert.assertSame(t, t2); + } + + @Test + public void testDistributionSummary() { + Registry r = new DefaultRegistry(clock); + DistributionSummary t = r.distributionSummary(r.createId("foo")); + t.record(42L); + Assert.assertEquals(t.count(), 1L); + + DistributionSummary t2 = r.distributionSummary(r.createId("foo")); + Assert.assertSame(t, t2); + } + + @Test(expected = IllegalStateException.class) + public void testRegisterBadTypeAccess() { + Registry r = new DefaultRegistry(clock); + Counter c = new DefaultCounter(clock, r.createId("foo")); + r.register(c); + r.counter(c.id()); + } + + @Test(expected = IllegalStateException.class) + public void testCounterBadTypeAccess() { + Registry r = new DefaultRegistry(clock); + r.counter(r.createId("foo")); + r.distributionSummary(r.createId("foo")); + } + + @Test(expected = IllegalStateException.class) + public void testTimerBadTypeAccess() { + Registry r = new DefaultRegistry(clock); + r.timer(r.createId("foo")); + r.counter(r.createId("foo")); + } + + @Test(expected = IllegalStateException.class) + public void testDistributionSummaryBadTypeAccess() { + Registry r = new DefaultRegistry(clock); + r.distributionSummary(r.createId("foo")); + r.timer(r.createId("foo")); + } + + @Test + public void testRegisterBadTypeAccessNoThrow() { + System.setProperty("spectator.api.propagateWarnings", "false"); + Registry r = new DefaultRegistry(clock); + Counter c = new DefaultCounter(clock, r.createId("foo")); + r.counter(c.id()); + r.register(c); + Assert.assertNotSame(r.get(c.id()), c); + } + + @Test + public void testCounterBadTypeAccessNoThrow() { + System.setProperty("spectator.api.propagateWarnings", "false"); + Registry r = new DefaultRegistry(clock); + r.counter(r.createId("foo")); + Assert.assertEquals(r.distributionSummary(r.createId("foo")), NoopDistributionSummary.INSTANCE); + } + + @Test + public void testTimerBadTypeAccessNoThrow() { + System.setProperty("spectator.api.propagateWarnings", "false"); + Registry r = new DefaultRegistry(clock); + r.timer(r.createId("foo")); + Assert.assertEquals(r.counter(r.createId("foo")), NoopCounter.INSTANCE); + } + + @Test + public void testDistributionSummaryBadTypeAccessNoThrow() { + System.setProperty("spectator.api.propagateWarnings", "false"); + Registry r = new DefaultRegistry(clock); + r.distributionSummary(r.createId("foo")); + Assert.assertEquals(r.timer(r.createId("foo")), NoopTimer.INSTANCE); + } + + @Test + public void testMaxLimitExceededCounter() { + System.setProperty("spectator.api.maxNumberOfMeters", "1"); + Registry r = new DefaultRegistry(clock); + Assert.assertNotSame(r.counter(r.createId("c1")), NoopCounter.INSTANCE); + Assert.assertSame(r.counter(r.createId("c2")), NoopCounter.INSTANCE); + Assert.assertNotSame(r.counter(r.createId("c1")), NoopCounter.INSTANCE); + } + + @Test + public void testMaxLimitExceededTimer() { + System.setProperty("spectator.api.maxNumberOfMeters", "1"); + Registry r = new DefaultRegistry(clock); + Assert.assertNotSame(r.timer(r.createId("c1")), NoopTimer.INSTANCE); + Assert.assertSame(r.timer(r.createId("c2")), NoopTimer.INSTANCE); + Assert.assertNotSame(r.timer(r.createId("c1")), NoopTimer.INSTANCE); + } + + @Test + public void testMaxLimitExceededDistributionSummary() { + System.setProperty("spectator.api.maxNumberOfMeters", "1"); + Registry r = new DefaultRegistry(clock); + Assert.assertNotSame(r.distributionSummary(r.createId("c1")), NoopDistributionSummary.INSTANCE); + Assert.assertSame(r.distributionSummary(r.createId("c2")), NoopDistributionSummary.INSTANCE); + Assert.assertNotSame(r.distributionSummary(r.createId("c1")), NoopDistributionSummary.INSTANCE); + } + + @Test + public void testMaxLimitExceededRegister() { + final AtomicInteger count = new AtomicInteger(0); + RegistryListener listener = new RegistryListener() { + public void onAdd(Meter m) { + count.incrementAndGet(); + } + }; + + System.setProperty("spectator.api.maxNumberOfMeters", "1"); + Registry r = new DefaultRegistry(clock); + r.addListener(listener); + Assert.assertEquals(count.get(), 0); + r.register(new DefaultCounter(clock, r.createId("c1"))); + Assert.assertEquals(count.get(), 1); + r.register(new DefaultCounter(clock, r.createId("c2"))); + Assert.assertEquals(count.get(), 1); + r.register(new DefaultCounter(clock, r.createId("c1"))); + Assert.assertEquals(count.get(), 2); + } + + @Test + public void testGet() { + Registry r = new DefaultRegistry(clock); + Counter c = r.counter(r.createId("foo")); + Meter m = r.get(c.id()); + Assert.assertSame(c, m); + } + + @Test + public void testIteratorEmpty() { + Registry r = new DefaultRegistry(clock); + for (Meter m : r) { + Assert.fail("should be empty, but found " + m.id()); + } + } + + @Test + public void testIterator() { + Registry r = new DefaultRegistry(clock); + r.counter(r.createId("foo")); + r.counter(r.createId("bar")); + Set expected = new HashSet<>(); + expected.add(r.createId("foo")); + expected.add(r.createId("bar")); + for (Meter m : r) { + expected.remove(m.id()); + } + Assert.assertTrue(expected.isEmpty()); + } + + @Test + public void testListener() { + final Set seen = new HashSet<>(); + RegistryListener listener = new RegistryListener() { + public void onAdd(Meter m) { + Assert.assertTrue(!seen.contains(m.id())); + seen.add(m.id()); + } + }; + + Registry r = new DefaultRegistry(clock); + r.counter(r.createId("pre")); + r.addListener(listener); + r.counter(r.createId("foo")); + r.timer(r.createId("bar")); + r.distributionSummary(r.createId("baz")); + r.removeListener(listener); + r.counter(r.createId("post")); + + Set expected = new HashSet<>(); + expected.add(r.createId("foo")); + expected.add(r.createId("bar")); + expected.add(r.createId("baz")); + + Assert.assertEquals(expected, seen); + } +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/DefaultTimerTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultTimerTest.java new file mode 100644 index 000000000..b7bf723b9 --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/DefaultTimerTest.java @@ -0,0 +1,131 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +@RunWith(JUnit4.class) +public class DefaultTimerTest { + + private final ManualClock clock = new ManualClock(); + + @Test + public void testInit() { + Timer t = new DefaultTimer(clock, NoopId.INSTANCE); + Assert.assertEquals(t.count(), 0L); + Assert.assertEquals(t.totalTime(), 0L); + } + + @Test + public void testRecord() { + Timer t = new DefaultTimer(clock, NoopId.INSTANCE); + t.record(42, TimeUnit.MILLISECONDS); + Assert.assertEquals(t.count(), 1L); + Assert.assertEquals(t.totalTime(), 42000000L); + } + + @Test + public void testRecordCallable() throws Exception { + Timer t = new DefaultTimer(clock, NoopId.INSTANCE); + clock.setMonotonicTime(100L); + int v = t.record(new Callable() { + public Integer call() throws Exception { + clock.setMonotonicTime(500L); + return 42; + } + }); + Assert.assertEquals(v, 42); + Assert.assertEquals(t.count(), 1L); + Assert.assertEquals(t.totalTime(), 400L); + } + + @Test + public void testRecordCallableException() throws Exception { + Timer t = new DefaultTimer(clock, NoopId.INSTANCE); + clock.setMonotonicTime(100L); + boolean seen = false; + try { + t.record(new Callable() { + public Integer call() throws Exception { + clock.setMonotonicTime(500L); + throw new RuntimeException("foo"); + } + }); + } catch (Exception e) { + seen = true; + } + Assert.assertTrue(seen); + Assert.assertEquals(t.count(), 1L); + Assert.assertEquals(t.totalTime(), 400L); + } + + @Test + public void testRecordRunnable() throws Exception { + Timer t = new DefaultTimer(clock, NoopId.INSTANCE); + clock.setMonotonicTime(100L); + t.record(new Runnable() { + public void run() { + clock.setMonotonicTime(500L); + } + }); + Assert.assertEquals(t.count(), 1L); + Assert.assertEquals(t.totalTime(), 400L); + } + + @Test + public void testRecordRunnableException() throws Exception { + Timer t = new DefaultTimer(clock, NoopId.INSTANCE); + clock.setMonotonicTime(100L); + boolean seen = false; + try { + t.record(new Runnable() { + public void run() { + clock.setMonotonicTime(500L); + throw new RuntimeException("foo"); + } + }); + } catch (Exception e) { + seen = true; + } + Assert.assertTrue(seen); + Assert.assertEquals(t.count(), 1L); + Assert.assertEquals(t.totalTime(), 400L); + } + + @Test + public void testMeasure() { + Timer t = new DefaultTimer(clock, new DefaultId("foo")); + t.record(42, TimeUnit.MILLISECONDS); + clock.setWallTime(3712345L); + for (Measurement m : t.measure()) { + Assert.assertEquals(m.timestamp(), 3712345L); + if (m.id().equals(t.id().withTag("statistic", "count"))) { + Assert.assertEquals(m.value(), 1.0, 0.1e-12); + } else if (m.id().equals(t.id().withTag("statistic", "totalTime"))) { + Assert.assertEquals(m.value(), 42e6, 0.1e-12); + } else { + Assert.fail("unexpected id: " + m.id()); + } + } + } + +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/ExtendedRegistryTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/ExtendedRegistryTest.java new file mode 100644 index 000000000..65dfd5a5c --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/ExtendedRegistryTest.java @@ -0,0 +1,192 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.atomic.AtomicLong; + +@RunWith(JUnit4.class) +public class ExtendedRegistryTest { + + @Test + public void testCreateIdArray() { + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + Id id1 = r.createId("foo", "bar", "baz", "k", "v"); + Id id2 = r.createId("foo", new TagList("k", "v", new TagList("bar", "baz"))); + Assert.assertEquals(id1, id2); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateIdArrayOdd() { + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + r.createId("foo", "bar", "baz", "k"); + } + + @Test + public void testCounterHelpers() { + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + Counter c1 = r.counter("foo", "bar", "baz", "k", "v"); + Counter c2 = r.counter("foo", new TagList("k", "v", new TagList("bar", "baz"))); + Counter c3 = r.counter("foo"); + Assert.assertSame(c1, c2); + Assert.assertNotSame(c1, c3); + } + + @Test + public void testDistributionSummaryHelpers() { + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + DistributionSummary c1 = r.distributionSummary("foo", "bar", "baz", "k", "v"); + DistributionSummary c2 = r.distributionSummary("foo", + new TagList("k", "v", new TagList("bar", "baz"))); + DistributionSummary c3 = r.distributionSummary("foo"); + Assert.assertSame(c1, c2); + Assert.assertNotSame(c1, c3); + } + + @Test + public void testTimerHelpers() { + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + Timer c1 = r.timer("foo", "bar", "baz", "k", "v"); + Timer c2 = r.timer("foo", new TagList("k", "v", new TagList("bar", "baz"))); + Timer c3 = r.timer("foo"); + Assert.assertSame(c1, c2); + Assert.assertNotSame(c1, c3); + } + + @Test + public void testLongTaskTimerHelpers() { + ManualClock clock = new ManualClock(); + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry(clock)); + LongTaskTimer c1 = r.longTaskTimer("foo", "bar", "baz", "k", "v"); + Meter m1 = r.get(c1.id()); + Assert.assertEquals(c1.id(), m1.id()); // registration + + LongTaskTimer c2 = r.longTaskTimer("foo", new TagList("k", "v", new TagList("bar", "baz"))); + Assert.assertEquals(c1.id(), c2.id()); + + long t1 = c1.start(); + long t2 = c2.start(); + clock.setMonotonicTime(1000L); + clock.setWallTime(1L); + DefaultLongTaskTimerTest.assertLongTaskTimer(r.get(c1.id()), 1L, 2, 2.0e-6); + + c1.stop(t1); + DefaultLongTaskTimerTest.assertLongTaskTimer(r.get(c1.id()), 1L, 1, 1.0e-6); + + c2.stop(t2); + DefaultLongTaskTimerTest.assertLongTaskTimer(r.get(c1.id()), 1L, 0, 0L); + } + + @Test + public void testGaugeHelpers() { + AtomicLong al1 = new AtomicLong(1L); + AtomicLong al2 = new AtomicLong(2L); + AtomicLong al4 = new AtomicLong(4L); + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + AtomicLong v1 = r.gauge(r.createId("foo", "bar", "baz", "k", "v"), al1); + AtomicLong v2 = r.gauge("foo", new TagList("k", "v", new TagList("bar", "baz")), al2); + AtomicLong v3 = r.gauge("foo", al4); + Assert.assertSame(v1, al1); + Assert.assertSame(v2, al2); + Assert.assertSame(v3, al4); + Id id1 = r.createId("foo", "bar", "baz", "k", "v"); + Id id2 = r.createId("foo"); + Assert.assertEquals(r.get(id1).measure().iterator().next().value(), 3.0, 1e-12); + Assert.assertEquals(r.get(id2).measure().iterator().next().value(), 4.0, 1e-12); + } + + @Test + public void testGaugeHelpersWithFunction() { + AtomicLong al1 = new AtomicLong(1L); + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry(new ManualClock(40, 0))); + DoubleFunction f = Functions.age(r.clock()); + AtomicLong v1 = r.gauge("foo", al1, f); + Assert.assertSame(v1, al1); + Id id1 = r.createId("foo"); + Assert.assertEquals(r.get(id1).measure().iterator().next().value(), 39.0 / 1000.0, 1e-12); + } + + @Test + public void testCollectionSizeHelpers() { + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + LinkedBlockingDeque q1 = new LinkedBlockingDeque<>(); + LinkedBlockingDeque q2 = r.collectionSize("queueSize", q1); + Assert.assertSame(q1, q2); + Id id = r.createId("queueSize"); + Assert.assertEquals(r.get(id).measure().iterator().next().value(), 0.0, 1e-12); + q2.push("foo"); + Assert.assertEquals(r.get(id).measure().iterator().next().value(), 1.0, 1e-12); + } + + @Test + public void testMapSizeHelpers() { + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + ConcurrentHashMap q1 = new ConcurrentHashMap<>(); + ConcurrentHashMap q2 = r.mapSize("mapSize", q1); + Assert.assertSame(q1, q2); + Id id = r.createId("mapSize"); + Assert.assertEquals(r.get(id).measure().iterator().next().value(), 0.0, 1e-12); + q2.put("foo", "bar"); + Assert.assertEquals(r.get(id).measure().iterator().next().value(), 1.0, 1e-12); + } + + @Test + public void testMethodValueHelpers() { + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + LinkedBlockingDeque q1 = new LinkedBlockingDeque<>(); + r.methodValue("queueSize", q1, "size"); + Id id = r.createId("queueSize"); + Assert.assertEquals(r.get(id).measure().iterator().next().value(), 0.0, 1e-12); + q1.push("foo"); + Assert.assertEquals(r.get(id).measure().iterator().next().value(), 1.0, 1e-12); + } + + @Test(expected = ClassCastException.class) + public void methodValueBadReturnType() { + System.setProperty("spectator.api.propagateWarnings", "true"); + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + r.methodValue("queueSize", this, "toString"); + } + + @Test + public void methodValueBadReturnTypeNoPropagate() { + System.setProperty("spectator.api.propagateWarnings", "false"); + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + r.methodValue("queueSize", this, "toString"); + Assert.assertNull(r.get(r.createId("queueSize"))); + } + + @Test(expected = RuntimeException.class) + public void methodValueUnknown() { + System.setProperty("spectator.api.propagateWarnings", "true"); + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + r.methodValue("queueSize", this, "unknownMethod"); + } + + @Test + public void methodValueUnknownNoPropagate() { + System.setProperty("spectator.api.propagateWarnings", "false"); + ExtendedRegistry r = new ExtendedRegistry(new DefaultRegistry()); + r.methodValue("queueSize", this, "unknownMethod"); + Assert.assertNull(r.get(r.createId("queueSize"))); + } +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/FunctionsTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/FunctionsTest.java new file mode 100644 index 000000000..6c587994b --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/FunctionsTest.java @@ -0,0 +1,135 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class FunctionsTest { + + private final ManualClock clock = new ManualClock(); + private final ExtendedRegistry registry = new ExtendedRegistry(new DefaultRegistry()); + + @Test + public void ageFunction() { + clock.setWallTime(5000L); + final DoubleFunction f = Functions.age(clock); + Assert.assertEquals(f.apply(1000L), 4.0, 1e-12); + } + + private byte byteMethod() { + return (byte) 1; + } + + @Test + public void invokeMethodByte() throws Exception { + final ValueFunction f = Functions.invokeMethod(registry.getMethod(getClass(), "byteMethod")); + Assert.assertEquals(f.apply(this), 1.0, 1e-12); + } + + private short shortMethod() { + return (short) 2; + } + + @Test + public void invokeMethodShort() throws Exception { + final ValueFunction f = Functions.invokeMethod(registry.getMethod(getClass(), "shortMethod")); + Assert.assertEquals(f.apply(this), 2.0, 1e-12); + } + + private int intMethod() { + return 3; + } + + @Test + public void invokeMethodInt() throws Exception { + final ValueFunction f = Functions.invokeMethod(registry.getMethod(getClass(), "intMethod")); + Assert.assertEquals(f.apply(this), 3.0, 1e-12); + } + + private long longMethod() { + return 4L; + } + + @Test + public void invokeMethodLong() throws Exception { + final ValueFunction f = Functions.invokeMethod(registry.getMethod(getClass(), "longMethod")); + Assert.assertEquals(f.apply(this), 4.0, 1e-12); + } + + private Long wrapperLongMethod() { + return 5L; + } + + @Test + public void invokeMethodWrapperLong() throws Exception { + final ValueFunction f = Functions.invokeMethod( + registry.getMethod(getClass(), "wrapperLongMethod")); + Assert.assertEquals(f.apply(this), 5.0, 1e-12); + } + + private Long throwsMethod() { + throw new IllegalStateException("fubar"); + } + + @Test + public void invokeBadMethod() throws Exception { + final ValueFunction f = Functions.invokeMethod(registry.getMethod(getClass(), "throwsMethod")); + Assert.assertEquals(f.apply(this), Double.NaN, 1e-12); + } + + @Test(expected = NoSuchMethodException.class) + public void invokeNoSuchMethod() throws Exception { + Functions.invokeMethod(registry.getMethod(getClass(), "unknownMethod")); + } + + @Test + public void invokeOnSubclass() throws Exception { + final ValueFunction f = Functions.invokeMethod(registry.getMethod(B.class, "two")); + Assert.assertEquals(f.apply(new B()), 2.0, 1e-12); + } + + @Test + public void invokeOneA() throws Exception { + final ValueFunction f = Functions.invokeMethod(registry.getMethod(A.class, "one")); + Assert.assertEquals(f.apply(new A()), 1.0, 1e-12); + } + + @Test + public void invokeOneB() throws Exception { + final ValueFunction f = Functions.invokeMethod(registry.getMethod(B.class, "one")); + Assert.assertEquals(f.apply(new B()), -1.0, 1e-12); + } + + private static class A { + public int one() { + return 1; + } + } + + private static class B extends A { + public int one() { + return -1; + } + + public int two() { + return 2; + } + } +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/MeasurementTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/MeasurementTest.java new file mode 100644 index 000000000..927142d03 --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/MeasurementTest.java @@ -0,0 +1,42 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import nl.jqno.equalsverifier.EqualsVerifier; +import nl.jqno.equalsverifier.Warning; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class MeasurementTest { + + @Test + public void testEqualsContract() { + EqualsVerifier + .forClass(Measurement.class) + .suppress(Warning.NULL_FIELDS) + .verify(); + } + + @Test + public void testToString() { + Id id = new DefaultId("foo"); + Measurement m = new Measurement(id, 42L, 42.0); + Assert.assertEquals(m.toString(), "Measurement(foo,42,42.0)"); + } +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/NoopCounterTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/NoopCounterTest.java new file mode 100644 index 000000000..26f754803 --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/NoopCounterTest.java @@ -0,0 +1,52 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class NoopCounterTest { + @Test + public void testId() { + Assert.assertEquals(NoopCounter.INSTANCE.id(), NoopId.INSTANCE); + Assert.assertTrue(!NoopCounter.INSTANCE.hasExpired()); + } + + @Test + public void testIncrement() { + NoopCounter c = NoopCounter.INSTANCE; + c.increment(); + Assert.assertEquals(c.count(), 0L); + } + + @Test + public void testIncrementAmount() { + NoopCounter c = NoopCounter.INSTANCE; + c.increment(42); + Assert.assertEquals(c.count(), 0L); + } + + @Test + public void testMeasure() { + NoopCounter c = NoopCounter.INSTANCE; + c.increment(42); + Assert.assertTrue(!c.measure().iterator().hasNext()); + } + +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/NoopDistributionSummaryTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/NoopDistributionSummaryTest.java new file mode 100644 index 000000000..ff0c35bcb --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/NoopDistributionSummaryTest.java @@ -0,0 +1,46 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class NoopDistributionSummaryTest { + @Test + public void testId() { + Assert.assertEquals(NoopDistributionSummary.INSTANCE.id(), NoopId.INSTANCE); + Assert.assertTrue(!NoopDistributionSummary.INSTANCE.hasExpired()); + } + + @Test + public void testIncrement() { + NoopDistributionSummary t = NoopDistributionSummary.INSTANCE; + t.record(42); + Assert.assertEquals(t.count(), 0L); + Assert.assertEquals(t.totalAmount(), 0L); + } + + @Test + public void testMeasure() { + NoopDistributionSummary t = NoopDistributionSummary.INSTANCE; + t.record(42); + Assert.assertTrue(!t.measure().iterator().hasNext()); + } + +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/NoopIdTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/NoopIdTest.java new file mode 100644 index 000000000..cb4d09566 --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/NoopIdTest.java @@ -0,0 +1,39 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class NoopIdTest { + @Test + public void testTags() { + Assert.assertTrue(!NoopId.INSTANCE.tags().iterator().hasNext()); + } + + @Test + public void testWithTag() { + Assert.assertEquals(NoopId.INSTANCE.withTag(new TagList("k", "v")), NoopId.INSTANCE); + } + + @Test + public void testToString() { + Assert.assertEquals(NoopId.INSTANCE.toString(), "noop"); + } +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/NoopRegistryTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/NoopRegistryTest.java new file mode 100644 index 000000000..d87c7fb30 --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/NoopRegistryTest.java @@ -0,0 +1,126 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.concurrent.TimeUnit; + +@RunWith(JUnit4.class) +public class NoopRegistryTest { + + private final ManualClock clock = new ManualClock(); + + @Test + public void testCreateId() { + Registry r = new NoopRegistry(); + Assert.assertEquals(r.createId("foo"), NoopId.INSTANCE); + } + + @Test + public void testCreateIdWithTags() { + Registry r = new NoopRegistry(); + TagList ts = new TagList("k", "v"); + Assert.assertEquals(r.createId("foo", ts), NoopId.INSTANCE); + } + + @Test + public void testRegister() { + Registry r = new NoopRegistry(); + Counter c = new DefaultCounter(clock, r.createId("foo")); + r.register(c); + Assert.assertNull(r.get(c.id())); + } + + @Test + public void testCounter() { + Registry r = new NoopRegistry(); + Counter c = r.counter(r.createId("foo")); + c.increment(); + Assert.assertEquals(c.count(), 0L); + + Counter c2 = r.counter(r.createId("foo")); + Assert.assertSame(c, c2); + } + + @Test + public void testTimer() { + Registry r = new NoopRegistry(); + Timer t = r.timer(r.createId("foo")); + t.record(42L, TimeUnit.MILLISECONDS); + Assert.assertEquals(t.count(), 0L); + + Timer t2 = r.timer(r.createId("foo")); + Assert.assertSame(t, t2); + } + + @Test + public void testDistributionSummary() { + Registry r = new NoopRegistry(); + DistributionSummary t = r.distributionSummary(r.createId("foo")); + t.record(42L); + Assert.assertEquals(t.count(), 0L); + + DistributionSummary t2 = r.distributionSummary(r.createId("foo")); + Assert.assertSame(t, t2); + } + + @Test + public void testGet() { + Registry r = new NoopRegistry(); + Counter c = r.counter(r.createId("foo")); + Assert.assertNull(r.get(c.id())); + } + + @Test + public void testIteratorEmpty() { + Registry r = new NoopRegistry(); + for (Meter m : r) { + Assert.fail("should be empty, but found " + m.id()); + } + } + + @Test + public void testIterator() { + Registry r = new NoopRegistry(); + r.counter(r.createId("foo")); + r.counter(r.createId("bar")); + for (Meter m : r) { + Assert.fail("should be empty, but found " + m.id()); + } + } + + @Test + public void testListener() { + RegistryListener listener = new RegistryListener() { + public void onAdd(Meter m) { + Assert.fail("shouldn't notify listeners"); + } + }; + + Registry r = new NoopRegistry(); + r.counter(r.createId("pre")); + r.addListener(listener); + r.counter(r.createId("foo")); + r.timer(r.createId("bar")); + r.distributionSummary(r.createId("baz")); + r.removeListener(listener); + r.counter(r.createId("post")); + } +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/NoopTimerTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/NoopTimerTest.java new file mode 100644 index 000000000..bf6961a54 --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/NoopTimerTest.java @@ -0,0 +1,48 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.concurrent.TimeUnit; + +@RunWith(JUnit4.class) +public class NoopTimerTest { + @Test + public void testId() { + Assert.assertEquals(NoopTimer.INSTANCE.id(), NoopId.INSTANCE); + Assert.assertTrue(!NoopTimer.INSTANCE.hasExpired()); + } + + @Test + public void testIncrement() { + NoopTimer t = NoopTimer.INSTANCE; + t.record(42, TimeUnit.MILLISECONDS); + Assert.assertEquals(t.count(), 0L); + Assert.assertEquals(t.totalTime(), 0L); + } + + @Test + public void testMeasure() { + NoopTimer t = NoopTimer.INSTANCE; + t.record(42, TimeUnit.MILLISECONDS); + Assert.assertTrue(!t.measure().iterator().hasNext()); + } + +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/ObjectGaugeTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/ObjectGaugeTest.java new file mode 100644 index 000000000..ce8ce7d80 --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/ObjectGaugeTest.java @@ -0,0 +1,46 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.concurrent.atomic.AtomicLong; + +@RunWith(JUnit4.class) +public class ObjectGaugeTest { + + private final ManualClock clock = new ManualClock(); + + @Test + public void testGC() { + ObjectGauge g = new ObjectGauge( + clock, NoopId.INSTANCE, new AtomicLong(42L), Functions.IDENTITY); + for (Measurement m : g.measure()) { + Assert.assertEquals(m.value(), 42.0, 1e-12); + } + + // Verify we get NaN after gc, this is quite possibly flakey and can be commented out + // if needed + System.gc(); + Assert.assertTrue(g.hasExpired()); + for (Measurement m : g.measure()) { + Assert.assertEquals(m.value(), Double.NaN, 1e-12); + } + } +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/SpectatorTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/SpectatorTest.java new file mode 100644 index 000000000..15f5f1aff --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/SpectatorTest.java @@ -0,0 +1,41 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class SpectatorTest { + @Test + public void testRegistry() { + Assert.assertNotNull(Spectator.registry()); + } + + @Test + public void testNewInstanceBadClass() { + System.setProperty("spectator.api.propagateWarnings", "false"); + Assert.assertTrue(Spectator.newInstance("fubar") instanceof DefaultRegistry); + } + + @Test(expected = RuntimeException.class) + public void testNewInstanceBadClassPropagate() { + System.setProperty("spectator.api.propagateWarnings", "true"); + Spectator.newInstance("fubar"); + } +} diff --git a/spectator-api/src/test/java/com/netflix/spectator/api/TagListTest.java b/spectator-api/src/test/java/com/netflix/spectator/api/TagListTest.java new file mode 100644 index 000000000..87a7cafb4 --- /dev/null +++ b/spectator-api/src/test/java/com/netflix/spectator/api/TagListTest.java @@ -0,0 +1,91 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +@RunWith(JUnit4.class) +public class TagListTest { + + @Test + public void equalsContractTest() { + // NOTE: EqualsVerifier doesn't work with cached hash code + TagList ts1 = new TagList("k1", "v1"); + TagList ts2 = new TagList("k2", "v2", ts1); + Assert.assertTrue(ts1.equals(ts1)); + Assert.assertTrue(ts2.equals(ts2)); + Assert.assertTrue(!ts1.equals(null)); + Assert.assertTrue(!ts1.equals(new Object())); + Assert.assertTrue(!ts1.equals(new TagList("k1", "v2"))); + Assert.assertTrue(!ts1.equals(new TagList("k2", "v1"))); + Assert.assertTrue(!ts1.equals(new TagList("k1", "v1", ts2))); + Assert.assertTrue(ts2.equals(new TagList("k2", "v2", ts1))); + Assert.assertTrue(ts2.equals(new TagList("k2", "v2", new TagList("k1", "v1")))); + } + + @Test + public void testSingle() { + TagList ts = new TagList("k", "v"); + for (Tag t : ts) { + Assert.assertEquals(t, ts); + Assert.assertEquals(t.key(), "k"); + Assert.assertEquals(t.value(), "v"); + } + } + + @Test(expected = NullPointerException.class) + public void testNullKey() { + new TagList(null, "v"); + } + + @Test(expected = NullPointerException.class) + public void testNullValue() { + new TagList("k", null); + } + + @Test + public void testCreateFromMap() { + Map m = new HashMap<>(); + m.put("k", "v"); + TagList ts1 = TagList.create(m); + TagList ts2 = new TagList("k", "v"); + Assert.assertEquals(ts1, ts2); + } + + @Test + public void testCreateFromTagList() { + TagList ts = new TagList("k", "v"); + TagList ts1 = TagList.create(ts); + TagList ts2 = new TagList("k", "v"); + Assert.assertEquals(ts1, ts2); + } + + @Test + public void testCreateFromIterable() { + Collection coll = Collections.singleton(new TagList("k", "v")); + TagList ts1 = TagList.create(coll); + TagList ts2 = new TagList("k", "v"); + Assert.assertEquals(ts1, ts2); + } +} diff --git a/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/CircularBuffer.java b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/CircularBuffer.java new file mode 100644 index 000000000..fdf7750d6 --- /dev/null +++ b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/CircularBuffer.java @@ -0,0 +1,64 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.gc; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceArray; + +/** + * Fixed size buffer that overwrites previous entries after filling up all slots. + */ +class CircularBuffer { + + private final AtomicInteger nextIndex; + private final AtomicReferenceArray data; + + /** Create a new instance. */ + CircularBuffer(int length) { + nextIndex = new AtomicInteger(0); + data = new AtomicReferenceArray<>(length); + } + + /** Add a new item to the buffer. If the buffer is full a previous entry will get overwritten. */ + void add(T item) { + int i = nextIndex.getAndIncrement() % data.length(); + data.set(i, item); + } + + /** Get the item in the buffer at position {@code i} or return null if it isn't set. */ + T get(int i) { + return data.get(i); + } + + /** Return the capacity of the buffer. */ + int size() { + return data.length(); + } + + /** Return a list with a copy of the data in the buffer. */ + List toList() { + List items = new ArrayList<>(data.length()); + for (int i = 0; i < data.length(); ++i) { + T item = data.get(i); + if (item != null) { + items.add(item); + } + } + return items; + } +} diff --git a/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcEvent.java b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcEvent.java new file mode 100644 index 000000000..9111c6802 --- /dev/null +++ b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcEvent.java @@ -0,0 +1,119 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.gc; + +import com.sun.management.GarbageCollectionNotificationInfo; +import com.sun.management.GcInfo; + +import java.util.Comparator; +import java.util.Date; + +/** + * Metadata about a garbage collection event. + */ +public class GcEvent { + + private final String name; + private final GarbageCollectionNotificationInfo info; + private final GcType type; + private final long startTime; + + /** + * Create a new instance. + * + * @param info + * The info object from the notification emitter on the + * {@link java.lang.management.GarbageCollectorMXBean}. + * @param startTime + * Start time in milliseconds since the epoch. Note the info object has a start time relative + * to the time the jvm process was started. + */ + public GcEvent(GarbageCollectionNotificationInfo info, long startTime) { + this.name = info.getGcName(); + this.info = info; + this.type = HelperFunctions.getGcType(name); + this.startTime = startTime; + } + + /** Type of GC event that occurred. */ + public GcType getType() { + return type; + } + + /** Name of the collector for the event. */ + public String getName() { + return name; + } + + /** Start time in milliseconds since the epoch. */ + public long getStartTime() { + return startTime; + } + + /** + * Info object from the {@link java.lang.management.GarbageCollectorMXBean} notification + * emitter. + */ + public GarbageCollectionNotificationInfo getInfo() { + return info; + } + + @Override + public String toString() { + GcInfo gcInfo = info.getGcInfo(); + long totalBefore = HelperFunctions.getTotalUsage(gcInfo.getMemoryUsageBeforeGc()); + long totalAfter = HelperFunctions.getTotalUsage(gcInfo.getMemoryUsageAfterGc()); + long max = HelperFunctions.getTotalMaxUsage(gcInfo.getMemoryUsageAfterGc()); + + String unit = "K"; + double cnv = 1000.0; + if (max > 1000000000L) { + unit = "G"; + cnv = 1e9; + } else if (max > 1000000L) { + unit = "M"; + cnv = 1e6; + } + + String change = String.format( + "%.1f%s => %.1f%s / %.1f%s", + totalBefore / cnv, unit, + totalAfter / cnv, unit, + max / cnv, unit); + String percentChange = String.format( + "%.1f%% => %.1f%%", 100.0 * totalBefore / max, 100.0 * totalAfter / max); + + final Date d = new Date(startTime); + return type.toString() + ": " + + name + ", id=" + gcInfo.getId() + ", at=" + d.toString() + + ", duration=" + gcInfo.getDuration() + "ms" + ", cause=[" + info.getGcCause() + "]" + + ", " + change + " (" + percentChange + ")"; + } + + /** Order events from oldest to newest. */ + public static final Comparator TIME_ORDER = new Comparator() { + public int compare(GcEvent e1, GcEvent e2) { + return (int) (e1.getStartTime() - e2.getStartTime()); + } + }; + + /** Order events from newest to oldest. */ + public static final Comparator REVERSE_TIME_ORDER = new Comparator() { + public int compare(GcEvent e1, GcEvent e2) { + return (int) (e2.getStartTime() - e1.getStartTime()); + } + }; +} diff --git a/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcEventListener.java b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcEventListener.java new file mode 100644 index 000000000..17fc545c9 --- /dev/null +++ b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcEventListener.java @@ -0,0 +1,22 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.gc; + +/** Listener for GC events. */ +public interface GcEventListener { + /** Invoked after a GC event occurs. */ + void onComplete(GcEvent event); +} diff --git a/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcLogger.java b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcLogger.java new file mode 100644 index 000000000..ee211a998 --- /dev/null +++ b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcLogger.java @@ -0,0 +1,220 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.gc; + +import com.netflix.spectator.api.Counter; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Spectator; +import com.netflix.spectator.api.Timer; +import com.netflix.spectator.impl.Preconditions; +import com.sun.management.GarbageCollectionNotificationInfo; +import com.sun.management.GcInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.ListenerNotFoundException; +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.openmbean.CompositeData; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Logger to collect GC notifcation events. + */ +public final class GcLogger { + + private static final Logger LOGGER = LoggerFactory.getLogger(GcLogger.class); + + // One major GC per hour would require 168 for a week + // One minor GC per minute would require 180 for three hours + private static final int BUFFER_SIZE = 256; + + // Max size of old generation memory pool + private static final AtomicLong MAX_DATA_SIZE = + Spectator.registry().gauge("jvm.gc.maxDataSize", new AtomicLong(0L)); + + // Size of old generation memory pool after a full GC + private static final AtomicLong LIVE_DATA_SIZE = + Spectator.registry().gauge("jvm.gc.liveDataSize", new AtomicLong(0L)); + + // Incremented for any positive increases in the size of the old generation memory pool + // before GC to after GC + private static final Counter PROMOTION_RATE = + Spectator.registry().counter("jvm.gc.promotionRate"); + + // Incremented for the increase in the size of the young generation memory pool after one GC + // to before the next + private static final Counter ALLOCATION_RATE = + Spectator.registry().counter("jvm.gc.allocationRate"); + + // Pause time due to GC event + private static final Id PAUSE_TIME = Spectator.registry().createId("jvm.gc.pause"); + + private final long jvmStartTime; + + private final ConcurrentHashMap> gcLogs = new ConcurrentHashMap<>(); + + private long youngGenSizeAfter = 0L; + + private String youngGenPoolName = null; + private String oldGenPoolName = null; + + private GcNotificationListener notifListener = null; + + private GcEventListener eventListener = null; + + /** Create a new instance. */ + public GcLogger() { + jvmStartTime = ManagementFactory.getRuntimeMXBean().getStartTime(); + for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) { + CircularBuffer buffer = new CircularBuffer<>(BUFFER_SIZE); + gcLogs.put(mbean.getName(), buffer); + } + + for (MemoryPoolMXBean mbean : ManagementFactory.getMemoryPoolMXBeans()) { + if (HelperFunctions.isYoungGenPool(mbean.getName())) { + youngGenPoolName = mbean.getName(); + } + if (HelperFunctions.isOldGenPool(mbean.getName())) { + oldGenPoolName = mbean.getName(); + } + } + } + + /** + * Start collecting data about GC events. + * + * @param listener + * If not null, the listener will be called with the event objects after metrics and the + * log buffer is updated. + */ + public synchronized void start(GcEventListener listener) { + Preconditions.checkState(notifListener == null, "logger already started"); + eventListener = listener; + notifListener = new GcNotificationListener(); + for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) { + if (mbean instanceof NotificationEmitter) { + final NotificationEmitter emitter = (NotificationEmitter) mbean; + emitter.addNotificationListener(notifListener, null, null); + } + } + } + + /** Stop collecting GC events. */ + public synchronized void stop() { + Preconditions.checkState(notifListener != null, "logger has not been started"); + for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) { + if (mbean instanceof NotificationEmitter) { + final NotificationEmitter emitter = (NotificationEmitter) mbean; + try { + emitter.removeNotificationListener(notifListener); + } catch (ListenerNotFoundException e) { + LOGGER.warn("could not remove gc listener", e); + } + } + } + notifListener = null; + } + + /** Return the current set of GC events in the in-memory log. */ + public List getLogs() { + final List logs = new ArrayList<>(); + for (CircularBuffer buffer : gcLogs.values()) { + logs.addAll(buffer.toList()); + } + Collections.sort(logs, GcEvent.REVERSE_TIME_ORDER); + return logs; + } + + private void updateMetrics(String name, GcInfo info) { + final Map before = info.getMemoryUsageBeforeGc(); + final Map after = info.getMemoryUsageAfterGc(); + + if (oldGenPoolName != null) { + final long oldBefore = before.get(oldGenPoolName).getUsed(); + final long oldAfter = after.get(oldGenPoolName).getUsed(); + final long delta = oldAfter - oldBefore; + if (delta > 0L) { + PROMOTION_RATE.increment(delta); + } + + if (HelperFunctions.getGcType(name) == GcType.OLD) { + LIVE_DATA_SIZE.set(oldAfter); + final long oldMaxAfter = after.get(oldGenPoolName).getMax(); + MAX_DATA_SIZE.set(oldMaxAfter); + } + } + + if (youngGenPoolName != null) { + final long youngBefore = before.get(youngGenPoolName).getUsed(); + final long youngAfter = after.get(youngGenPoolName).getUsed(); + final long delta = youngBefore - youngGenSizeAfter; + youngGenSizeAfter = youngAfter; + if (delta > 0L) { + ALLOCATION_RATE.increment(delta); + } + } + } + + private void processGcEvent(GarbageCollectionNotificationInfo info) { + GcEvent event = new GcEvent(info, jvmStartTime + info.getGcInfo().getStartTime()); + gcLogs.get(info.getGcName()).add(event); + if (LOGGER.isInfoEnabled()) { + LOGGER.info(event.toString()); + } + + // Update pause timer for the action and cause... + Id eventId = PAUSE_TIME + .withTag("action", info.getGcAction()) + .withTag("cause", info.getGcCause()); + Timer timer = Spectator.registry().timer(eventId); + timer.record(info.getGcInfo().getDuration(), TimeUnit.MILLISECONDS); + + // Update promotion and allocation counters + updateMetrics(info.getGcName(), info.getGcInfo()); + + // Notify an event listener if registered + if (eventListener != null) { + try { + eventListener.onComplete(event); + } catch (Exception e) { + LOGGER.warn("exception thrown by event listener", e); + } + } + } + + private class GcNotificationListener implements NotificationListener { + public void handleNotification(Notification notification, Object ref) { + final String type = notification.getType(); + if (type.equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { + CompositeData cd = (CompositeData) notification.getUserData(); + GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from(cd); + processGcEvent(info); + } + } + } +} diff --git a/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcType.java b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcType.java new file mode 100644 index 000000000..4b7dce360 --- /dev/null +++ b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcType.java @@ -0,0 +1,30 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.gc; + +/** + * Simple classification of gc type to avoid reliance on names than can vary. + */ +public enum GcType { + /** Major collection. */ + OLD, + + /** Minor collection. */ + YOUNG, + + /** Could not determine the collection type. */ + UNKNOWN +} diff --git a/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/HelperFunctions.java b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/HelperFunctions.java new file mode 100644 index 000000000..136d20d11 --- /dev/null +++ b/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/HelperFunctions.java @@ -0,0 +1,89 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.gc; + +import com.sun.management.GcInfo; + +import java.lang.management.MemoryUsage; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** Utility functions for GC. */ +final class HelperFunctions { + + private static final Map KNOWN_COLLECTOR_NAMES = knownCollectors(); + + private HelperFunctions() { + } + + private static Map knownCollectors() { + Map m = new HashMap<>(); + m.put("ConcurrentMarkSweep", GcType.OLD); + m.put("Copy", GcType.YOUNG); + m.put("G1 Old Generation", GcType.OLD); + m.put("G1 Young Generation", GcType.YOUNG); + m.put("MarkSweepCompact", GcType.OLD); + m.put("PS MarkSweep", GcType.OLD); + m.put("PS Scavenge", GcType.YOUNG); + m.put("ParNew", GcType.YOUNG); + return Collections.unmodifiableMap(m); + } + + /** Determine the type, old or young, based on the name of the collector. */ + static GcType getGcType(String name) { + GcType t = KNOWN_COLLECTOR_NAMES.get(name); + return (t == null) ? GcType.UNKNOWN : t; + } + + /** Returns true if memory pool name matches an old generation pool. */ + static boolean isOldGenPool(String name) { + return name.endsWith("Old Gen") || name.endsWith("Tenured Gen"); + } + + /** Returns true if memory pool name matches an young generation pool. */ + static boolean isYoungGenPool(String name) { + return name.endsWith("Eden Space"); + } + + /** Compute the total usage across all pools. */ + static long getTotalUsage(Map usages) { + long sum = 0L; + for (Map.Entry e : usages.entrySet()) { + sum += e.getValue().getUsed(); + } + return sum; + } + + /** Compute the max usage across all pools. */ + static long getTotalMaxUsage(Map usages) { + long sum = 0L; + for (Map.Entry e : usages.entrySet()) { + long max = e.getValue().getMax(); + if (max > 0) { + sum += e.getValue().getMax(); + } + } + return sum; + } + + /** Compute the amount of data promoted during a GC event. */ + static long getPromotionSize(GcInfo info) { + long totalBefore = getTotalUsage(info.getMemoryUsageBeforeGc()); + long totalAfter = getTotalUsage(info.getMemoryUsageAfterGc()); + return totalAfter - totalBefore; + } +} diff --git a/spectator-nflx/src/main/java/com/netflix/spectator/nflx/ChronosGcEventListener.java b/spectator-nflx/src/main/java/com/netflix/spectator/nflx/ChronosGcEventListener.java new file mode 100644 index 000000000..ab0ae9aa6 --- /dev/null +++ b/spectator-nflx/src/main/java/com/netflix/spectator/nflx/ChronosGcEventListener.java @@ -0,0 +1,131 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.nflx; + +import com.netflix.client.http.HttpRequest; +import com.netflix.client.http.HttpResponse; +import com.netflix.config.DynamicBooleanProperty; +import com.netflix.config.DynamicPropertyFactory; +import com.netflix.niws.client.http.RestClient; +import com.netflix.spectator.gc.GcEvent; +import com.netflix.spectator.gc.GcEventListener; +import com.netflix.spectator.ribbon.RestClientFactory; +import com.sun.management.GcInfo; +import org.codehaus.jackson.map.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +/** + * Listener that sends GC events to a chronos backend. + */ +public class ChronosGcEventListener implements GcEventListener { + + private static final DynamicBooleanProperty ENABLED = + DynamicPropertyFactory.getInstance().getBooleanProperty("spectator.gc.chronosEnabled", true); + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final ObjectMapper mapper = new ObjectMapper(); + + private final ExecutorService executor = Executors.newSingleThreadExecutor( + new ThreadFactory() { + public Thread newThread(Runnable r) { + Thread t = new Thread(r, "ChronosGcEventListener"); + t.setDaemon(true); + return t; + } + }); + + private final RestClient client = RestClientFactory.getClient("chronos_gc"); + + private String getenv(String k) { + String v = System.getenv(k); + return (v == null || v.length() == 0) ? "unknown" : v; + } + + /** Convert a GC event into a map. */ + Map toGcInfoMap(GcEvent event) { + final GcInfo info = event.getInfo().getGcInfo(); + Map map = new HashMap<>(); + map.put("id", info.getId()); + map.put("startTime", event.getStartTime()); + map.put("endTime", event.getStartTime() + info.getEndTime()); + map.put("duration", info.getDuration()); + map.put("memoryBeforeGc", info.getMemoryUsageBeforeGc()); + map.put("memoryAfterGc", info.getMemoryUsageAfterGc()); + return map; + } + + /** Convert a GC event into a map. */ + Map toEventMap(GcEvent event) { + Map map = new HashMap<>(); + map.put("action", event.getInfo().getGcAction()); + map.put("cause", event.getInfo().getGcCause()); + map.put("name", event.getName()); + map.put("gcInfo", toGcInfoMap(event)); + map.put("app", getenv("NETFLIX_APP")); + map.put("cluster", getenv("NETFLIX_CLUSTER")); + map.put("asg", getenv("NETFLIX_AUTO_SCALE_GROUP")); + map.put("region", getenv("EC2_REGION")); + map.put("zone", getenv("EC2_AVAILABILITY_ZONE")); + map.put("ami", getenv("EC2_AMI_ID")); + map.put("node", getenv("EC2_INSTANCE_ID")); + return map; + } + + @Override + public void onComplete(final GcEvent event) { + if (!ENABLED.get()) { + return; + } + + try { + final byte[] json = mapper.writeValueAsBytes(toEventMap(event)); + executor.submit(new Runnable() { + public void run() { + HttpRequest request = new HttpRequest.Builder() + .verb(HttpRequest.Verb.POST) + .uri(URI.create("/api/v2/event")) + .header("Content-Type", "application/json") + .entity(json) + .build(); + try (HttpResponse response = client.executeWithLoadBalancer(request)) { + if (response.getStatus() != 200) { + logger.warn("failed to send GC event to chronos (status={})", response.getStatus()); + } + } catch (Exception e) { + logger.warn("failed to send GC event to chronos", e); + } + } + }); + } catch (IOException e) { + logger.warn("failed to send GC event to chronos", e); + } + } + + /** Shutdown the executor used to send data to chronos. */ + public void shutdown() { + executor.shutdown(); + } +} diff --git a/spectator-nflx/src/main/java/com/netflix/spectator/nflx/Plugin.java b/spectator-nflx/src/main/java/com/netflix/spectator/nflx/Plugin.java new file mode 100644 index 000000000..b3ef4e90f --- /dev/null +++ b/spectator-nflx/src/main/java/com/netflix/spectator/nflx/Plugin.java @@ -0,0 +1,51 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.nflx; + +import com.netflix.config.ConfigurationManager; +import com.netflix.governator.annotations.AutoBindSingleton; +import com.netflix.spectator.gc.GcLogger; +import org.apache.commons.configuration.AbstractConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.PostConstruct; +import java.io.IOException; + +/** + * Plugin for setting up spectator to report correctly into the standard Netflix stack. + */ +@AutoBindSingleton +public final class Plugin { + + private static final String CONFIG_FILE = "spectator.properties"; + + private static final GcLogger GC_LOGGER = new GcLogger(); + + private static final Logger LOGGER = LoggerFactory.getLogger(Plugin.class); + + @PostConstruct + private void init() throws IOException { + ConfigurationManager.loadPropertiesFromResources(CONFIG_FILE); + AbstractConfiguration config = ConfigurationManager.getConfigInstance(); + if (config.getBoolean("spectator.gc.loggingEnabled")) { + GC_LOGGER.start(new ChronosGcEventListener()); + LOGGER.info("gc logging started"); + } else { + LOGGER.info("gc logging is not enabled"); + } + } +} diff --git a/spectator-nflx/src/main/java/com/netflix/spectator/ribbon/MeteredRestClient.java b/spectator-nflx/src/main/java/com/netflix/spectator/ribbon/MeteredRestClient.java new file mode 100644 index 000000000..f12f2d2f2 --- /dev/null +++ b/spectator-nflx/src/main/java/com/netflix/spectator/ribbon/MeteredRestClient.java @@ -0,0 +1,98 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.ribbon; + +import com.netflix.client.ClientException; +import com.netflix.client.config.IClientConfig; +import com.netflix.client.http.HttpRequest; +import com.netflix.client.http.HttpResponse; +import com.netflix.niws.client.http.RestClient; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Spectator; +import com.netflix.spectator.api.Timer; + +import java.util.concurrent.TimeUnit; + +/** + * Subclass that provides instrumentation of requests, latency, and failures. + */ +public class MeteredRestClient extends RestClient { + + private Timer latency; + + private Id requests; + private Id exceptions; + + private Id niwsRequests; + private Id niwsExceptions; + + @Override + public void initWithNiwsConfig(IClientConfig config) { + super.initWithNiwsConfig(config); + + final String client = "client"; + final String cname = getClientName(); + latency = Spectator.registry().timer("ribbon.http.latency", client, cname); + + requests = Spectator.registry().createId("ribbon.http.requests", client, cname); + exceptions = Spectator.registry().createId("ribbon.http.exceptions", client, cname); + + niwsRequests = Spectator.registry().createId("ribbon.http.niwsRequests", client, cname); + niwsExceptions = Spectator.registry().createId("ribbon.http.niwsExceptions", client, cname); + } + + @Override + public HttpResponse execute(HttpRequest req) throws Exception { + final long start = System.nanoTime(); + try { + final HttpResponse res = super.execute(req); + final String status = String.format("%d", res.getStatus()); + Spectator.registry().counter(requests.withTag("status", status)).increment(); + return res; + } catch (ClientException e) { + final String m = e.getErrorType().name(); + Spectator.registry().counter(exceptions.withTag("error", m)).increment(); + throw e; + } catch (Exception e) { + final String c = e.getClass().getSimpleName(); + Spectator.registry().counter(exceptions.withTag("error", c)).increment(); + throw e; + } finally { + latency.record(System.nanoTime() - start, TimeUnit.NANOSECONDS); + } + } + + @Override + public HttpResponse executeWithLoadBalancer(HttpRequest req) throws ClientException { + final long start = System.nanoTime(); + try { + final HttpResponse res = super.executeWithLoadBalancer(req); + final String status = String.format("%d", res.getStatus()); + Spectator.registry().counter(niwsRequests.withTag("status", status)).increment(); + return res; + } catch (ClientException e) { + final String m = e.getErrorType().name(); + Spectator.registry().counter(niwsExceptions.withTag("error", m)).increment(); + throw e; + } catch (Exception e) { + final String c = e.getClass().getSimpleName(); + Spectator.registry().counter(niwsExceptions.withTag("error", c)).increment(); + throw e; + } finally { + latency.record(System.nanoTime() - start, TimeUnit.NANOSECONDS); + } + } +} diff --git a/spectator-nflx/src/main/java/com/netflix/spectator/ribbon/RestClientFactory.java b/spectator-nflx/src/main/java/com/netflix/spectator/ribbon/RestClientFactory.java new file mode 100644 index 000000000..fcb61d0d1 --- /dev/null +++ b/spectator-nflx/src/main/java/com/netflix/spectator/ribbon/RestClientFactory.java @@ -0,0 +1,49 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.ribbon; + +import com.netflix.client.ClientFactory; +import com.netflix.niws.client.http.RestClient; + +/** + * Helper for creating a {@link com.netflix.niws.client.http.RestClient} using the spectator + * client config implementation. + */ +public final class RestClientFactory { + private RestClientFactory() { + } + + /** + * Get or create a {@link com.netflix.niws.client.http.RestClient} with the specified name. The + * client will use the {@link com.netflix.spectator.ribbon.RibbonClientConfigImpl} that changes + * some of the defaults to make the common cases work easier: + * + *
    + *
  • Namespace for the clients defaults to {@code niws.client} to avoid property name changes + * if switching between internal {@code platform-ipc} and {@code ribbon}.
  • + *
  • The default server list class is set to {@code DiscoveryEnabledNIWSServerList}.
  • + *
  • An instrumented RestClient class is returned.
  • + *
+ * + * @param name + * Name of the client to retrieve. + * @return + * Rest client for the specified name. + */ + public static RestClient getClient(String name) { + return (RestClient) ClientFactory.getNamedClient(name, RibbonClientConfigImpl.class); + } +} diff --git a/spectator-nflx/src/main/java/com/netflix/spectator/ribbon/RibbonClientConfigImpl.java b/spectator-nflx/src/main/java/com/netflix/spectator/ribbon/RibbonClientConfigImpl.java new file mode 100644 index 000000000..19f086f56 --- /dev/null +++ b/spectator-nflx/src/main/java/com/netflix/spectator/ribbon/RibbonClientConfigImpl.java @@ -0,0 +1,39 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.ribbon; + +import com.netflix.client.config.DefaultClientConfigImpl; + +/** + * Customize some of the default settings used for rest clients. + */ +public class RibbonClientConfigImpl extends DefaultClientConfigImpl { + + @Override + public String getNameSpace() { + return "niws.client"; + } + + @Override + public String getDefaultClientClassname() { + return "com.netflix.spectator.ribbon.MeteredRestClient"; + } + + @Override + public String getDefaultSeverListClass() { + return "com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList"; + } +} diff --git a/spectator-nflx/src/main/resources/spectator.properties b/spectator-nflx/src/main/resources/spectator.properties new file mode 100644 index 000000000..8c2f3cf42 --- /dev/null +++ b/spectator-nflx/src/main/resources/spectator.properties @@ -0,0 +1,32 @@ +# +# Copyright 2014 Netflix, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +# Should we enable gc logging? Only checked at startup. +spectator.gc.loggingEnabled=true + +# Should we send gc events to chronos backend? Logging must be enabled. This property is only +# checked at startup. +spectator.gc.chronosEnabled=true + +# Rest client for chronos gc backend +chronos_gc.niws.client.AppName=CHRONOS_BACKEND +chronos_gc.niws.client.ReadTimeout=15000 +chronos_gc.niws.client.ConnectTimeout=5000 +chronos_gc.niws.client.MaxAutoRetries=0 +chronos_gc.niws.client.MaxAutoRetriesNextServer=2 +chronos_gc.niws.client.OkToRetryOnAllOperations=true +chronos_gc.niws.client.DeploymentContextBasedVipAddresses=chronos_backend-gc:7001 diff --git a/spectator-nflx/src/test/java/com/netflix/spectator/ribbon/MeteredRestClientTest.java b/spectator-nflx/src/test/java/com/netflix/spectator/ribbon/MeteredRestClientTest.java new file mode 100644 index 000000000..307325941 --- /dev/null +++ b/spectator-nflx/src/test/java/com/netflix/spectator/ribbon/MeteredRestClientTest.java @@ -0,0 +1,133 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.ribbon; + +import com.netflix.client.http.HttpRequest; +import com.netflix.client.http.HttpResponse; +import com.netflix.niws.client.http.RestClient; +import com.netflix.spectator.api.ExtendedRegistry; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Spectator; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.URI; + +@RunWith(JUnit4.class) +public class MeteredRestClientTest { + + private static final String client = "MeteredRestClientTest"; + + private static HttpServer server; + private static int port; + + @BeforeClass + public static void startServer() throws Exception { + server = HttpServer.create(new InetSocketAddress(0), 0); + port = server.getAddress().getPort(); + + server.createContext("/ok", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + exchange.sendResponseHeaders(200, 0L); + exchange.close(); + } + }); + + server.start(); + + System.setProperty(client + ".niws.client.NIWSServerListClassName", + "com.netflix.loadbalancer.ConfigurationBasedServerList"); + System.setProperty(client + ".niws.client.listOfServers", + "localhost:" + port); + } + + @AfterClass + public static void stopServer() { + server.stop(0); + } + + private int get(String loc) { + URI uri = URI.create(loc); + HttpRequest req = new HttpRequest.Builder() + .verb(HttpRequest.Verb.GET) + .uri(uri) + .build(); + RestClient c = RestClientFactory.getClient(client); + try (HttpResponse res = uri.isAbsolute() ? c.execute(req) : c.executeWithLoadBalancer(req)) { + return res.getStatus(); + } catch (Exception e) { + e.printStackTrace(); + return -1; + } + } + + private long reqCount(int status) { + ExtendedRegistry r = Spectator.registry(); + Id requests = r.createId("ribbon.http.requests", "client", client, "status", "" + status); + return r.counter(requests).count(); + } + + private long niwsReqCount(int status) { + ExtendedRegistry r = Spectator.registry(); + Id requests = r.createId("ribbon.http.niwsRequests", "client", client, "status", "" + status); + return r.counter(requests).count(); + } + + @Test + public void executeOk() { + long before = reqCount(200); + Assert.assertEquals(get("http://localhost:" + port + "/ok"), 200); + Assert.assertEquals(reqCount(200), before + 1); + } + + @Test + public void executeNotFound() { + long before200 = reqCount(200); + long before404 = reqCount(404); + Assert.assertEquals(get("http://localhost:" + port + "/not-found"), 404); + Assert.assertEquals(reqCount(200), before200); + Assert.assertEquals(reqCount(404), before404 + 1); + } + + @Test + public void executeWithLbOk() { + long before = reqCount(200); + long nbefore = niwsReqCount(200); + Assert.assertEquals(get("/ok"), 200); + Assert.assertEquals(reqCount(200), before + 1); + Assert.assertEquals(niwsReqCount(200), nbefore + 1); + } + + @Test + public void executeWithLbNotFound() { + long before200 = niwsReqCount(200); + long before404 = niwsReqCount(404); + Assert.assertEquals(get("/not-found"), 404); + Assert.assertEquals(niwsReqCount(200), before200); + Assert.assertEquals(niwsReqCount(404), before404 + 1); + } +} + diff --git a/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsCounter.java b/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsCounter.java new file mode 100644 index 000000000..de72bf4fa --- /dev/null +++ b/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsCounter.java @@ -0,0 +1,76 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.metrics2; + +import com.netflix.spectator.api.Clock; +import com.netflix.spectator.api.Counter; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Measurement; + +import java.util.Collections; + +/** Counter implementation for the metrics2 registry. */ +class MetricsCounter implements Counter { + + private final Clock clock; + private final Id id; + private final com.yammer.metrics.core.Meter impl; + + /** Create a new instance. */ + MetricsCounter(Clock clock, Id id, com.yammer.metrics.core.Meter impl) { + this.clock = clock; + this.id = id; + this.impl = impl; + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + long now = clock.wallTime(); + long v = impl.count(); + return Collections.singleton(new Measurement(id, now, v)); + } + + /** {@inheritDoc} */ + @Override + public void increment() { + impl.mark(); + } + + /** {@inheritDoc} */ + @Override + public void increment(long amount) { + impl.mark(amount); + } + + /** {@inheritDoc} */ + @Override + public long count() { + return impl.count(); + } +} diff --git a/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsDistributionSummary.java b/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsDistributionSummary.java new file mode 100644 index 000000000..78e5baf72 --- /dev/null +++ b/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsDistributionSummary.java @@ -0,0 +1,75 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.metrics2; + +import com.netflix.spectator.api.Clock; +import com.netflix.spectator.api.DistributionSummary; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Measurement; + +import java.util.Collections; + +/** Distribution summary implementation for the metric2 registry. */ +class MetricsDistributionSummary implements DistributionSummary { + + private final Clock clock; + private final Id id; + private final com.yammer.metrics.core.Histogram impl; + + /** Create a new instance. */ + MetricsDistributionSummary(Clock clock, Id id, com.yammer.metrics.core.Histogram impl) { + this.clock = clock; + this.id = id; + this.impl = impl; + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void record(long amount) { + impl.update(amount); + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + final long now = clock.wallTime(); + return Collections.singleton(new Measurement(id, now, impl.mean())); + } + + /** {@inheritDoc} */ + @Override + public long count() { + return impl.count(); + } + + /** {@inheritDoc} */ + @Override + public long totalAmount() { + return (long) impl.sum(); + } +} diff --git a/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsRegistry.java b/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsRegistry.java new file mode 100644 index 000000000..06f1d6b5f --- /dev/null +++ b/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsRegistry.java @@ -0,0 +1,72 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.metrics2; + +import com.netflix.spectator.api.*; +import com.yammer.metrics.Metrics; +import com.yammer.metrics.core.MetricName; + +import java.util.concurrent.TimeUnit; + +/** Registry implementation that maps spectator types to the metrics2 library. */ +public class MetricsRegistry extends AbstractRegistry { + + private final com.yammer.metrics.core.MetricsRegistry impl; + + /** Create a new instance. */ + public MetricsRegistry() { + this(Clock.SYSTEM, Metrics.defaultRegistry()); + } + + /** Create a new instance. */ + public MetricsRegistry(Clock clock, com.yammer.metrics.core.MetricsRegistry impl) { + super(clock); + this.impl = impl; + } + + private MetricName toMetricName(Id id) { + final String name = id.name(); + final int pos = name.lastIndexOf("."); + if (pos != -1) { + final String prefix = name.substring(0, pos); + final String suffix = name.substring(pos + 1); + return new MetricName("spectator", prefix, suffix); + } else { + return new MetricName("spectator", "default", id.name()); + } + } + + /** {@inheritDoc} */ + @Override + protected Counter newCounter(Id id) { + final MetricName name = toMetricName(id); + return new MetricsCounter(clock(), id, impl.newMeter(name, "calls", TimeUnit.SECONDS)); + } + + /** {@inheritDoc} */ + @Override + protected DistributionSummary newDistributionSummary(Id id) { + final MetricName name = toMetricName(id); + return new MetricsDistributionSummary(clock(), id, impl.newHistogram(name, false)); + } + + /** {@inheritDoc} */ + @Override + protected Timer newTimer(Id id) { + final MetricName name = toMetricName(id); + return new MetricsTimer(clock(), id, impl.newTimer(name, TimeUnit.SECONDS, TimeUnit.SECONDS)); + } +} diff --git a/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsTimer.java b/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsTimer.java new file mode 100644 index 000000000..dc2aecd21 --- /dev/null +++ b/spectator-reg-metrics2/src/main/java/com/netflix/spectator/metrics2/MetricsTimer.java @@ -0,0 +1,101 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.metrics2; + +import com.netflix.spectator.api.Clock; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Measurement; +import com.netflix.spectator.api.Timer; + +import java.util.Collections; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +/** Timer implementation for the metrics2 registry. */ +class MetricsTimer implements Timer { + + private final Clock clock; + private final Id id; + private final com.yammer.metrics.core.Timer impl; + + /** Create a new instance. */ + MetricsTimer(Clock clock, Id id, com.yammer.metrics.core.Timer impl) { + this.clock = clock; + this.id = id; + this.impl = impl; + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void record(long amount, TimeUnit unit) { + impl.update(amount, unit); + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + final long now = clock.wallTime(); + return Collections.singleton(new Measurement(id, now, impl.meanRate())); + } + + /** {@inheritDoc} */ + @Override + public T record(Callable f) throws Exception { + final long s = clock.monotonicTime(); + try { + return f.call(); + } finally { + final long e = clock.monotonicTime(); + record(e - s, TimeUnit.NANOSECONDS); + } + } + + /** {@inheritDoc} */ + @Override + public void record(Runnable f) { + final long s = clock.monotonicTime(); + try { + f.run(); + } finally { + final long e = clock.monotonicTime(); + record(e - s, TimeUnit.NANOSECONDS); + } + } + + /** {@inheritDoc} */ + @Override + public long count() { + return impl.count(); + } + + /** {@inheritDoc} */ + @Override + public long totalTime() { + return (long) impl.sum(); + } +} diff --git a/spectator-reg-metrics2/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry b/spectator-reg-metrics2/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry new file mode 100644 index 000000000..dc8ac67dc --- /dev/null +++ b/spectator-reg-metrics2/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry @@ -0,0 +1 @@ +com.netflix.spectator.metrics2.MetricsRegistry diff --git a/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsCounter.java b/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsCounter.java new file mode 100644 index 000000000..08e8d33bb --- /dev/null +++ b/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsCounter.java @@ -0,0 +1,76 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.metrics3; + +import com.netflix.spectator.api.Clock; +import com.netflix.spectator.api.Counter; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Measurement; + +import java.util.Collections; + +/** Counter implementation for the metric3 registry. */ +class MetricsCounter implements Counter { + + private final Clock clock; + private final Id id; + private final com.codahale.metrics.Meter impl; + + /** Create a new instance. */ + MetricsCounter(Clock clock, Id id, com.codahale.metrics.Meter impl) { + this.clock = clock; + this.id = id; + this.impl = impl; + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + long now = clock.wallTime(); + long v = impl.getCount(); + return Collections.singleton(new Measurement(id, now, v)); + } + + /** {@inheritDoc} */ + @Override + public void increment() { + impl.mark(); + } + + /** {@inheritDoc} */ + @Override + public void increment(long amount) { + impl.mark(amount); + } + + /** {@inheritDoc} */ + @Override + public long count() { + return impl.getCount(); + } +} diff --git a/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsDistributionSummary.java b/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsDistributionSummary.java new file mode 100644 index 000000000..7d103ed32 --- /dev/null +++ b/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsDistributionSummary.java @@ -0,0 +1,80 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.metrics3; + +import com.codahale.metrics.Snapshot; +import com.netflix.spectator.api.Clock; +import com.netflix.spectator.api.DistributionSummary; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Measurement; + +import java.util.Collections; +import java.util.concurrent.atomic.AtomicLong; + +/** Distribution summary implementation for the metric3 registry. */ +class MetricsDistributionSummary implements DistributionSummary { + + private final Clock clock; + private final Id id; + private final com.codahale.metrics.Histogram impl; + private final AtomicLong totalAmount; + + /** Create a new instance. */ + MetricsDistributionSummary(Clock clock, Id id, com.codahale.metrics.Histogram impl) { + this.clock = clock; + this.id = id; + this.impl = impl; + this.totalAmount = new AtomicLong(0L); + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void record(long amount) { + impl.update(amount); + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + final long now = clock.wallTime(); + final Snapshot snapshot = impl.getSnapshot(); + return Collections.singleton(new Measurement(id, now, snapshot.getMean())); + } + + /** {@inheritDoc} */ + @Override + public long count() { + return impl.getCount(); + } + + /** {@inheritDoc} */ + @Override + public long totalAmount() { + return totalAmount.get(); + } +} diff --git a/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsRegistry.java b/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsRegistry.java new file mode 100644 index 000000000..3666693e2 --- /dev/null +++ b/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsRegistry.java @@ -0,0 +1,65 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.metrics3; + +import com.netflix.spectator.api.*; + +/** Registry implementation that maps spectator types to the metrics3 library. */ +public class MetricsRegistry extends AbstractRegistry { + + private final com.codahale.metrics.MetricRegistry impl; + + /** Create a new instance. */ + public MetricsRegistry() { + this(Clock.SYSTEM, new com.codahale.metrics.MetricRegistry()); + } + + /** Create a new instance. */ + public MetricsRegistry(Clock clock, com.codahale.metrics.MetricRegistry impl) { + super(clock); + this.impl = impl; + } + + private String toMetricName(Id id) { + StringBuilder buf = new StringBuilder(); + buf.append(id.name()); + for (Tag t : id.tags()) { + buf.append(t.key()).append("-").append(t.value()); + } + return buf.toString(); + } + + /** {@inheritDoc} */ + @Override + protected Counter newCounter(Id id) { + final String name = toMetricName(id); + return new MetricsCounter(clock(), id, impl.meter(name)); + } + + /** {@inheritDoc} */ + @Override + protected DistributionSummary newDistributionSummary(Id id) { + final String name = toMetricName(id); + return new MetricsDistributionSummary(clock(), id, impl.histogram(name)); + } + + /** {@inheritDoc} */ + @Override + protected Timer newTimer(Id id) { + final String name = toMetricName(id); + return new MetricsTimer(clock(), id, impl.timer(name)); + } +} diff --git a/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsTimer.java b/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsTimer.java new file mode 100644 index 000000000..67779a36e --- /dev/null +++ b/spectator-reg-metrics3/src/main/java/com/netflix/spectator/metrics3/MetricsTimer.java @@ -0,0 +1,104 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.metrics3; + +import com.netflix.spectator.api.Clock; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Measurement; +import com.netflix.spectator.api.Timer; + +import java.util.Collections; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** Timer implementation for the metrics3 registry. */ +class MetricsTimer implements Timer { + + private final Clock clock; + private final Id id; + private final com.codahale.metrics.Timer impl; + private final AtomicLong totalTime; + + /** Create a new instance. */ + MetricsTimer(Clock clock, Id id, com.codahale.metrics.Timer impl) { + this.clock = clock; + this.id = id; + this.impl = impl; + this.totalTime = new AtomicLong(0L); + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void record(long amount, TimeUnit unit) { + impl.update(amount, unit); + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + final long now = clock.wallTime(); + return Collections.singleton(new Measurement(id, now, impl.getMeanRate())); + } + + /** {@inheritDoc} */ + @Override + public T record(Callable f) throws Exception { + final long s = clock.monotonicTime(); + try { + return f.call(); + } finally { + final long e = clock.monotonicTime(); + record(e - s, TimeUnit.NANOSECONDS); + } + } + + /** {@inheritDoc} */ + @Override + public void record(Runnable f) { + final long s = clock.monotonicTime(); + try { + f.run(); + } finally { + final long e = clock.monotonicTime(); + record(e - s, TimeUnit.NANOSECONDS); + } + } + + /** {@inheritDoc} */ + @Override + public long count() { + return impl.getCount(); + } + + /** {@inheritDoc} */ + @Override + public long totalTime() { + return totalTime.get(); + } +} diff --git a/spectator-reg-metrics3/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry b/spectator-reg-metrics3/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry new file mode 100644 index 000000000..de9ad58ee --- /dev/null +++ b/spectator-reg-metrics3/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry @@ -0,0 +1 @@ +com.netflix.spectator.metrics3.MetricsRegistry diff --git a/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoCounter.java b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoCounter.java new file mode 100644 index 000000000..c27ec79c7 --- /dev/null +++ b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoCounter.java @@ -0,0 +1,90 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.servo; + +import com.netflix.servo.monitor.Monitor; +import com.netflix.spectator.api.Clock; +import com.netflix.spectator.api.Counter; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Measurement; + +import java.util.Collections; +import java.util.concurrent.atomic.AtomicLong; + +/** Counter implementation for the servo registry. */ +class ServoCounter implements Counter, ServoMeter { + + private final Clock clock; + private final ServoId id; + private final com.netflix.servo.monitor.Counter impl; + + // Local count so that we have more flexibility on servo counter impl without changing the + // value returned by the {@link #count()} method. + private final AtomicLong count; + + /** Create a new instance. */ + ServoCounter(Clock clock, ServoId id, com.netflix.servo.monitor.Counter impl) { + this.clock = clock; + this.id = id; + this.impl = impl; + this.count = new AtomicLong(0L); + } + + @Override + public Monitor monitor() { + return impl; + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + long now = clock.wallTime(); + long v = count.get(); + return Collections.singleton(new Measurement(id, now, v)); + } + + /** {@inheritDoc} */ + @Override + public void increment() { + impl.increment(); + count.incrementAndGet(); + } + + /** {@inheritDoc} */ + @Override + public void increment(long amount) { + impl.increment(amount); + count.addAndGet(amount); + } + + /** {@inheritDoc} */ + @Override + public long count() { + return count.get(); + } +} diff --git a/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoDistributionSummary.java b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoDistributionSummary.java new file mode 100644 index 000000000..d74c68c69 --- /dev/null +++ b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoDistributionSummary.java @@ -0,0 +1,102 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.servo; + +import com.netflix.servo.monitor.BasicDistributionSummary; +import com.netflix.servo.monitor.Monitor; +import com.netflix.spectator.api.Clock; +import com.netflix.spectator.api.DistributionSummary; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Measurement; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +/** Distribution summary implementation for the servo registry. */ +class ServoDistributionSummary implements DistributionSummary, ServoMeter { + + private final Clock clock; + private final ServoId id; + private final BasicDistributionSummary impl; + + // Local count so that we have more flexibility on servo counter impl without changing the + // value returned by the {@link #count()} method. + private final AtomicLong count; + private final AtomicLong totalAmount; + + private final Id countId; + private final Id totalAmountId; + + /** Create a new instance. */ + ServoDistributionSummary(Clock clock, ServoId id, BasicDistributionSummary impl) { + this.clock = clock; + this.id = id; + this.impl = impl; + this.count = new AtomicLong(0L); + this.totalAmount = new AtomicLong(0L); + countId = id.withTag("statistic", "count"); + totalAmountId = id.withTag("statistic", "totalAmount"); + } + + /** {@inheritDoc} */ + @Override + public Monitor monitor() { + return impl; + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void record(long amount) { + impl.record(amount); + totalAmount.addAndGet(amount); + count.incrementAndGet(); + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + final long now = clock.wallTime(); + final List ms = new ArrayList<>(2); + ms.add(new Measurement(countId, now, count.get())); + ms.add(new Measurement(totalAmountId, now, totalAmount.get())); + return ms; + } + + /** {@inheritDoc} */ + @Override + public long count() { + return count.get(); + } + + /** {@inheritDoc} */ + @Override + public long totalAmount() { + return totalAmount.get(); + } +} diff --git a/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoId.java b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoId.java new file mode 100644 index 000000000..2d03a021f --- /dev/null +++ b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoId.java @@ -0,0 +1,80 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.servo; + +import com.netflix.servo.monitor.MonitorConfig; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Tag; + +import java.util.Iterator; + +/** Id implementation for the servo registry. */ +class ServoId implements Id { + + private final MonitorConfig config; + + /** Create a new instance. */ + public ServoId(MonitorConfig config) { + this.config = config; + } + + /** Return the monitor config this id is based on. */ + MonitorConfig config() { + return config; + } + + /** {@inheritDoc} */ + @Override + public String name() { + return config.getName(); + } + + /** {@inheritDoc} */ + @Override + public Iterable tags() { + return new Iterable() { + public Iterator iterator() { + return new Iterator() { + private final Iterator iter = config.getTags().iterator(); + + public boolean hasNext() { + return iter.hasNext(); + } + + public Tag next() { + return new ServoTag(iter.next()); + } + + public void remove() { + iter.remove(); + } + }; + } + }; + } + + /** {@inheritDoc} */ + @Override + public Id withTag(String k, String v) { + return new ServoId((new MonitorConfig.Builder(config)).withTag(k, v).build()); + } + + /** {@inheritDoc} */ + @Override + public Id withTag(Tag t) { + return withTag(t.key(), t.value()); + } +} diff --git a/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoMeter.java b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoMeter.java new file mode 100644 index 000000000..373565b25 --- /dev/null +++ b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoMeter.java @@ -0,0 +1,24 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.servo; + +import com.netflix.servo.monitor.Monitor; + +/** Meter that can return a servo monitor. */ +interface ServoMeter { + /** Returns the monitor corresponding to this meter. */ + Monitor monitor(); +} diff --git a/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoRegistry.java b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoRegistry.java new file mode 100644 index 000000000..4d7e310e9 --- /dev/null +++ b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoRegistry.java @@ -0,0 +1,118 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.servo; + +import com.netflix.servo.DefaultMonitorRegistry; +import com.netflix.servo.monitor.*; +import com.netflix.spectator.api.*; +import com.netflix.spectator.api.Counter; +import com.netflix.spectator.api.Timer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** Registry that maps spectator types to servo. */ +public class ServoRegistry extends AbstractRegistry implements CompositeMonitor { + + private static final MonitorConfig DEFAULT_CONFIG = + (new MonitorConfig.Builder("spectator.registry")).build(); + + private final MonitorConfig config; + + /** Create a new instance. */ + public ServoRegistry() { + this(Clock.SYSTEM); + } + + /** Create a new instance. */ + public ServoRegistry(Clock clock) { + this(clock, DEFAULT_CONFIG); + } + + /** Create a new instance. */ + ServoRegistry(Clock clock, MonitorConfig config) { + super(clock); + this.config = config; + DefaultMonitorRegistry.getInstance().register(this); + } + + private MonitorConfig toMonitorConfig(Id id) { + MonitorConfig.Builder builder = new MonitorConfig.Builder(id.name()); + for (Tag t : id.tags()) { + builder.withTag(t.key(), t.value()); + } + return builder.build(); + } + + /** {@inheritDoc} */ + @Override + protected Counter newCounter(Id id) { + MonitorConfig cfg = toMonitorConfig(id); + StepCounter counter = new StepCounter(cfg); + return new ServoCounter(clock(), new ServoId(cfg), counter); + } + + /** {@inheritDoc} */ + @Override + protected DistributionSummary newDistributionSummary(Id id) { + MonitorConfig cfg = toMonitorConfig(id); + BasicDistributionSummary distributionSummary = new BasicDistributionSummary(cfg); + return new ServoDistributionSummary(clock(), new ServoId(cfg), distributionSummary); + } + + /** {@inheritDoc} */ + @Override + protected Timer newTimer(Id id) { + MonitorConfig cfg = toMonitorConfig(id); + BasicTimer timer = new BasicTimer(cfg, TimeUnit.SECONDS); + return new ServoTimer(clock(), new ServoId(cfg), timer); + } + + /** {@inheritDoc} */ + @Override + public Integer getValue() { + return 0; + } + + /** {@inheritDoc} */ + @Override + public Integer getValue(int pollerIndex) { + return 0; + } + + /** {@inheritDoc} */ + @Override + public MonitorConfig getConfig() { + return config; + } + + /** {@inheritDoc} */ + @Override + public List> getMonitors() { + List> monitors = new ArrayList<>(); + for (Meter meter : this) { + if (meter instanceof ServoMeter) { + monitors.add(((ServoMeter) meter).monitor()); + } else { + for (Measurement m : meter.measure()) { + monitors.add(new NumberGauge(toMonitorConfig(m.id()), m.value())); + } + } + } + return monitors; + } +} diff --git a/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoTag.java b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoTag.java new file mode 100644 index 000000000..43f538081 --- /dev/null +++ b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoTag.java @@ -0,0 +1,41 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.servo; + +import com.netflix.spectator.api.Tag; + +/** Tag implementation for the servo registry. */ +class ServoTag implements Tag { + + private final com.netflix.servo.tag.Tag tag; + + /** Create a new instance. */ + public ServoTag(com.netflix.servo.tag.Tag tag) { + this.tag = tag; + } + + /** {@inheritDoc} */ + @Override + public String key() { + return tag.getKey(); + } + + /** {@inheritDoc} */ + @Override + public String value() { + return tag.getValue(); + } +} diff --git a/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoTimer.java b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoTimer.java new file mode 100644 index 000000000..e7d42b6de --- /dev/null +++ b/spectator-reg-servo/src/main/java/com/netflix/spectator/servo/ServoTimer.java @@ -0,0 +1,128 @@ +/** + * Copyright 2014 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.spectator.servo; + +import com.netflix.servo.monitor.Monitor; +import com.netflix.spectator.api.Clock; +import com.netflix.spectator.api.Id; +import com.netflix.spectator.api.Measurement; +import com.netflix.spectator.api.Timer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +/** Timer implementation for the servo registry. */ +class ServoTimer implements Timer, ServoMeter { + + private final Clock clock; + private final ServoId id; + private final com.netflix.servo.monitor.Timer impl; + + // Local count so that we have more flexibility on servo counter impl without changing the + // value returned by the {@link #count()} method. + private final AtomicLong count; + private final AtomicLong totalTime; + + private final Id countId; + private final Id totalTimeId; + + /** Create a new instance. */ + ServoTimer(Clock clock, ServoId id, com.netflix.servo.monitor.Timer impl) { + this.clock = clock; + this.id = id; + this.impl = impl; + this.count = new AtomicLong(0L); + this.totalTime = new AtomicLong(0L); + countId = id.withTag("statistic", "count"); + totalTimeId = id.withTag("statistic", "totalTime"); + } + + /** {@inheritDoc} */ + @Override + public Monitor monitor() { + return impl; + } + + /** {@inheritDoc} */ + @Override + public Id id() { + return id; + } + + /** {@inheritDoc} */ + @Override + public boolean hasExpired() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void record(long amount, TimeUnit unit) { + final long nanos = unit.toNanos(amount); + impl.record(amount, unit); + totalTime.addAndGet(nanos); + count.incrementAndGet(); + } + + /** {@inheritDoc} */ + @Override + public Iterable measure() { + final long now = clock.wallTime(); + final List ms = new ArrayList<>(2); + ms.add(new Measurement(countId, now, count.get())); + ms.add(new Measurement(totalTimeId, now, totalTime.get())); + return ms; + } + + /** {@inheritDoc} */ + @Override + public T record(Callable f) throws Exception { + final long s = clock.monotonicTime(); + try { + return f.call(); + } finally { + final long e = clock.monotonicTime(); + record(e - s, TimeUnit.NANOSECONDS); + } + } + + /** {@inheritDoc} */ + @Override + public void record(Runnable f) { + final long s = clock.monotonicTime(); + try { + f.run(); + } finally { + final long e = clock.monotonicTime(); + record(e - s, TimeUnit.NANOSECONDS); + } + } + + /** {@inheritDoc} */ + @Override + public long count() { + return count.get(); + } + + /** {@inheritDoc} */ + @Override + public long totalTime() { + return totalTime.get(); + } +} diff --git a/spectator-reg-servo/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry b/spectator-reg-servo/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry new file mode 100644 index 000000000..a764a73fb --- /dev/null +++ b/spectator-reg-servo/src/main/resources/META-INF/services/com.netflix.spectator.api.Registry @@ -0,0 +1 @@ +com.netflix.spectator.servo.ServoRegistry