Skip to content

Commit

Permalink
Merge pull request #445 from brharrington/delegate-to-spectator
Browse files Browse the repository at this point in the history
add support to delegate to spectator
  • Loading branch information
dmuino authored May 10, 2018
2 parents 710a42e + beea181 commit d13c088
Show file tree
Hide file tree
Showing 21 changed files with 804 additions and 112 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ subprojects {
dependencies {
compile 'org.slf4j:slf4j-api'
compile 'com.google.guava:guava'
compile 'com.netflix.spectator:spectator-api:0.66.0'
testCompile 'org.testng:testng:6.1.1'
testRuntime 'org.slf4j:slf4j-log4j12'
testRuntime 'log4j:log4j:1.2.17'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2013 Netflix, Inc.
/*
* Copyright 2011-2018 Netflix, Inc.
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -118,12 +118,12 @@ private static ObjectNameMapper getObjectNameMapper(Properties props) {

return mapper;
}

private static Properties loadProps() {
String registryClassProp = System.getProperty(REGISTRY_CLASS_PROP);
String registryNameProp = System.getProperty(REGISTRY_NAME_PROP);
String registryJmxNameProp = System.getProperty(REGISTRY_JMX_NAME_PROP);

Properties props = new Properties();
if (registryClassProp != null) {
props.setProperty(REGISTRY_CLASS_PROP, registryClassProp);
Expand All @@ -150,6 +150,7 @@ public Collection<Monitor<?>> getRegisteredMonitors() {
*/
@Override
public void register(Monitor<?> monitor) {
SpectatorContext.register(monitor);
registry.register(monitor);
}

Expand All @@ -158,6 +159,7 @@ public void register(Monitor<?> monitor) {
*/
@Override
public void unregister(Monitor<?> monitor) {
SpectatorContext.unregister(monitor);
registry.unregister(monitor);
}

Expand Down
181 changes: 181 additions & 0 deletions servo-core/src/main/java/com/netflix/servo/SpectatorContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* Copyright 2011-2018 Netflix, Inc.
* <p/>
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.servo;

import com.netflix.servo.monitor.CompositeMonitor;
import com.netflix.servo.monitor.Monitor;
import com.netflix.servo.monitor.MonitorConfig;
import com.netflix.servo.monitor.SpectatorMonitor;
import com.netflix.spectator.api.Counter;
import com.netflix.spectator.api.DistributionSummary;
import com.netflix.spectator.api.Gauge;
import com.netflix.spectator.api.Id;
import com.netflix.spectator.api.Measurement;
import com.netflix.spectator.api.Meter;
import com.netflix.spectator.api.NoopRegistry;
import com.netflix.spectator.api.Registry;
import com.netflix.spectator.api.Timer;
import com.netflix.spectator.api.patterns.PolledMeter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
* Helper that can be used to delegate to spectator. Other than calling
* {@link #setRegistry(Registry)} to set the registry to use, it is only intended for use
* within servo.
*/
public final class SpectatorContext {

private SpectatorContext() {
}

private static final ScheduledExecutorService GAUGE_POOL = Executors.newScheduledThreadPool(
2,
task -> {
Thread t = new Thread(task, "servo-gauge-poller");
t.setDaemon(true);
return t;
}
);

private static final Logger LOGGER = LoggerFactory.getLogger(SpectatorContext.class);

private static volatile Registry registry = new NoopRegistry();

/**
* Set the registry to use. By default it will use the NoopRegistry.
*/
public static void setRegistry(Registry registry) {
SpectatorContext.registry = registry;
}

/**
* Get the registry that was configured.
*/
public static Registry getRegistry() {
return registry;
}

/** Create a gauge based on the config. */
public static Gauge gauge(MonitorConfig config) {
return registry.gauge(createId(config));
}

/** Create a max gauge based on the config. */
public static Gauge maxGauge(MonitorConfig config) {
return registry.maxGauge(createId(config));
}

/** Create a counter based on the config. */
public static Counter counter(MonitorConfig config) {
return registry.counter(createId(config));
}

/** Create a timer based on the config. */
public static Timer timer(MonitorConfig config) {
return registry.timer(createId(config));
}

/** Create a distribution summary based on the config. */
public static DistributionSummary distributionSummary(MonitorConfig config) {
return registry.distributionSummary(createId(config));
}

/** Convert servo config to spectator id. */
public static Id createId(MonitorConfig config) {
return registry
.createId(config.getName())
.withTags(config.getTags().asMap());
}

/** Dedicated thread pool for polling user defined functions registered as gauges. */
public static ScheduledExecutorService gaugePool() {
return GAUGE_POOL;
}

/** Create builder for a polled gauge based on the config. */
public static PolledMeter.Builder polledGauge(MonitorConfig config) {
return PolledMeter.using(registry)
.withId(createId(config))
.scheduleOn(GAUGE_POOL);
}

/** Register a custom monitor. */
public static void register(Monitor<?> monitor) {
PolledMeter.monitorMeter(registry, new ServoMeter(monitor));
}

/** Unregister a custom monitor. */
public static void unregister(Monitor<?> monitor) {
PolledMeter.remove(registry, createId(monitor.getConfig()));
}

private static class ServoMeter implements Meter {

private final Id id;
private final Monitor<?> monitor;

ServoMeter(Monitor<?> monitor) {
this.id = createId(monitor.getConfig());
this.monitor = monitor;
}

@Override public Id id() {
return id;
}

private void addMeasurements(Monitor<?> m, List<Measurement> measurements) {
// Skip any that will report directly
if (!(m instanceof SpectatorMonitor)) {
if (m instanceof CompositeMonitor<?>) {
CompositeMonitor<?> cm = (CompositeMonitor<?>) m;
for (Monitor<?> v : cm.getMonitors()) {
addMeasurements(v, measurements);
}
} else {
try {
Object obj = m.getValue();
if (obj instanceof Number) {
double value = ((Number) obj).doubleValue();
// timestamp will get ignored as the value will get forwarded to a gauge
Measurement v = new Measurement(createId(m.getConfig()), 0L, value);
measurements.add(v);
}
} catch (Throwable t) {
LOGGER.warn("Exception while querying user defined gauge ({}), "
+ "the value will be ignored. The owner of the user defined "
+ "function should fix it to not propagate an exception.", m.getConfig(), t);
}
}
}
}

@Override public Iterable<Measurement> measure() {
List<Measurement> measurements = new ArrayList<>();
addMeasurements(monitor, measurements);
return measurements;
}

@Override public boolean hasExpired() {
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2013 Netflix, Inc.
/*
* Copyright 2011-2018 Netflix, Inc.
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,6 +15,7 @@
*/
package com.netflix.servo.monitor;

import com.netflix.servo.SpectatorContext;
import com.netflix.servo.util.Throwables;

import java.lang.reflect.AccessibleObject;
Expand All @@ -24,7 +25,8 @@
/**
* Wraps an annotated field and exposes it as a numeric monitor object.
*/
class AnnotatedNumberMonitor extends AbstractMonitor<Number> implements NumericMonitor<Number> {
class AnnotatedNumberMonitor extends AbstractMonitor<Number>
implements NumericMonitor<Number>, SpectatorMonitor {

private final Object object;
private final AccessibleObject field;
Expand All @@ -33,6 +35,13 @@ class AnnotatedNumberMonitor extends AbstractMonitor<Number> implements NumericM
super(config);
this.object = object;
this.field = field;
if ("COUNTER".equals(config.getTags().getValue("type"))) {
SpectatorContext.polledGauge(config)
.monitorMonotonicCounter(this, m -> m.getValue(0).longValue());
} else {
SpectatorContext.polledGauge(config)
.monitorValue(this, m -> m.getValue(0).doubleValue());
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.netflix.servo.monitor;

import com.netflix.servo.SpectatorContext;
import com.netflix.servo.annotations.DataSourceType;

import java.util.concurrent.atomic.AtomicLong;
Expand All @@ -24,21 +25,25 @@
* The value is the total count for the life of the counter. Observers are responsible
* for converting to a rate and handling overflows if they occur.
*/
public final class BasicCounter extends AbstractMonitor<Number> implements Counter {
public final class BasicCounter extends AbstractMonitor<Number>
implements Counter, SpectatorMonitor {
private final AtomicLong count = new AtomicLong();
private final com.netflix.spectator.api.Counter spectatorCounter;

/**
* Creates a new instance of the counter.
*/
public BasicCounter(MonitorConfig config) {
super(config.withAdditionalTag(DataSourceType.COUNTER));
spectatorCounter = SpectatorContext.counter(config);
}

/**
* {@inheritDoc}
*/
@Override
public void increment() {
spectatorCounter.increment();
count.incrementAndGet();
}

Expand All @@ -47,6 +52,7 @@ public void increment() {
*/
@Override
public void increment(long amount) {
spectatorCounter.increment(amount);
count.getAndAdd(amount);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2013 Netflix, Inc.
/*
* Copyright 2011-2018 Netflix, Inc.
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,6 +15,7 @@
*/
package com.netflix.servo.monitor;

import com.netflix.servo.SpectatorContext;
import com.netflix.servo.annotations.DataSourceType;
import com.netflix.servo.util.Throwables;

Expand All @@ -23,7 +24,8 @@
/**
* A gauge implementation that invokes a specified callable to get the current value.
*/
public final class BasicGauge<T extends Number> extends AbstractMonitor<T> implements Gauge<T> {
public final class BasicGauge<T extends Number> extends AbstractMonitor<T>
implements Gauge<T>, SpectatorMonitor {
private final Callable<T> function;

/**
Expand All @@ -35,6 +37,8 @@ public final class BasicGauge<T extends Number> extends AbstractMonitor<T> imple
public BasicGauge(MonitorConfig config, Callable<T> function) {
super(config.withAdditionalTag(DataSourceType.GAUGE));
this.function = function;
SpectatorContext.polledGauge(config)
.monitorValue(this, m -> m.getValue(0).doubleValue());
}

/**
Expand Down
Loading

0 comments on commit d13c088

Please sign in to comment.