Skip to content

Commit

Permalink
Merge remote-tracking branch 'nflx/master' into max-poll
Browse files Browse the repository at this point in the history
  • Loading branch information
brharrington committed May 28, 2014
2 parents f967174 + 3128289 commit 377a8ca
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
package com.netflix.servo;

import com.netflix.servo.jmx.JmxMonitorRegistry;
import com.netflix.servo.jmx.ObjectNameMapper;
import com.netflix.servo.monitor.Monitor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -29,13 +31,21 @@
* specified registry class must have a constructor with no arguments. If the
* property is not specified or the class cannot be loaded an instance of
* {@link com.netflix.servo.jmx.JmxMonitorRegistry} will be used.
* <p>
* If the default {@link com.netflix.servo.jmx.JmxMonitorRegistry} is used, the property
* {@code com.netflix.servo.DefaultMonitorRegistry.jmxMapperClass} can optionally be
* specified to control how monitors are mapped to JMX {@link javax.management.ObjectName}.
* This property specifies the {@link com.netflix.servo.jmx.ObjectNameMapper}
* implementation class to use. The implementation must have a constructor with
* no arguments.
*/
public final class DefaultMonitorRegistry implements MonitorRegistry {

private static final Logger LOG = LoggerFactory.getLogger(DefaultMonitorRegistry.class);
private static final String CLASS_NAME = DefaultMonitorRegistry.class.getCanonicalName();
private static final String REGISTRY_CLASS_PROP = CLASS_NAME + ".registryClass";
private static final String REGISTRY_NAME_PROP = CLASS_NAME + ".registryName";
private static final String REGISTRY_JMX_NAME_PROP = CLASS_NAME + ".jmxMapperClass";
private static final MonitorRegistry INSTANCE = new DefaultMonitorRegistry();
private static final String DEFAULT_REGISTRY_NAME = "com.netflix.servo";

Expand Down Expand Up @@ -77,8 +87,36 @@ public static MonitorRegistry getInstance() {
}
registry = r;
} else {
registry = new JmxMonitorRegistry(registryName);
registry = new JmxMonitorRegistry(registryName,
getObjectNameMapper(props));
}
}

/**
* Gets the {@link ObjectNameMapper} to use by looking at the
* {@code com.netflix.servo.DefaultMonitorRegistry.jmxMapperClass}
* property. If not specified, then {@link ObjectNameMapper#DEFAULT}
* is used.
* @param props the properties
* @return the mapper to use
*/
private static ObjectNameMapper getObjectNameMapper(Properties props) {
ObjectNameMapper mapper = ObjectNameMapper.DEFAULT;
final String jmxNameMapperClass = props.getProperty(REGISTRY_JMX_NAME_PROP);
if (jmxNameMapperClass != null) {
try {
Class<?> mapperClazz = Class.forName(jmxNameMapperClass);
mapper = (ObjectNameMapper) mapperClazz.newInstance();
} catch (Throwable t) {
LOG.error(
"failed to create the JMX ObjectNameMapper instance of class "
+ jmxNameMapperClass
+ ", using the default naming scheme",
t);
}
}

return mapper;
}

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

import com.netflix.servo.jsr166e.ConcurrentHashMapV8;
import com.netflix.servo.tag.BasicTagList;
import com.netflix.servo.tag.TagList;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* This class creates a {@link java.lang.reflect.Proxy} monitor that tracks all calls to methods
Expand Down Expand Up @@ -51,19 +52,21 @@ public final class TimedInterface {
static final String CLASS_TAG = "class";
static final String ID_TAG = "id";
private static class TimedHandler<T> implements InvocationHandler, CompositeMonitor<Long> {
final T concrete;
private final T concrete;
private final Map<String, Timer> timers;
private final MonitorConfig baseConfig;
private final TagList baseTagList;

/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public List<Monitor<?>> getMonitors() {
List dynamicTimers = new ArrayList();
List<Monitor<?>> dynamicTimers = new ArrayList<Monitor<?>>();
for (Timer timer : timers.values()) {
dynamicTimers.add(timer);
}
return (List<Monitor<?>>) dynamicTimers;
return dynamicTimers;
}

@Override
Expand All @@ -84,10 +87,6 @@ public MonitorConfig getConfig() {
return baseConfig;
}

final ConcurrentHashMapV8<String, Timer> timers;
final MonitorConfig baseConfig;
final TagList baseTagList;

TimedHandler(Class<T> ctype, T concrete, String id) {
this.concrete = concrete;
BasicTagList tagList = BasicTagList.of(
Expand All @@ -99,7 +98,14 @@ public MonitorConfig getConfig() {
baseTagList = tagList;
baseConfig = MonitorConfig.builder(TIMED_INTERFACE).withTags(baseTagList).build();

timers = new ConcurrentHashMapV8<String, Timer>();
timers = new HashMap<String, Timer>();
for (Method method : ctype.getMethods()) {
final MonitorConfig config =
MonitorConfig.builder(method.getName())
.withTags(baseTagList)
.build();
timers.put(method.getName(), new BasicTimer(config));
}
}


Expand All @@ -108,18 +114,10 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
// if the method is one of the CompositeMonitor interface
final Class<?> declaringClass = method.getDeclaringClass();
if (declaringClass.isAssignableFrom(CompositeMonitor.class)) {
return method.invoke(this);
return method.invoke(this, args);
}
final String methodName = method.getName();
final Timer timer = timers.computeIfAbsent(methodName, new ConcurrentHashMapV8.Fun<String, Timer>() {
@Override
public Timer apply(String method) {
final MonitorConfig config = MonitorConfig.builder(method)
.withTags(baseTagList)
.build();
return new BasicTimer(config);
}
});
final Timer timer = timers.get(methodName);
final Stopwatch stopwatch = timer.start();
try {
return method.invoke(concrete, args);
Expand All @@ -139,8 +137,8 @@ private TimedInterface() {
*/
@SuppressWarnings("unchecked")
public static <T> T newProxy(Class<T> ctype, T concrete, String id) {
final InvocationHandler handler = new TimedHandler(ctype, concrete, id);
final Class[] types = new Class[] {ctype, CompositeMonitor.class};
final InvocationHandler handler = new TimedHandler<T>(ctype, concrete, id);
final Class<?>[] types = new Class[] {ctype, CompositeMonitor.class};
return (T) Proxy.newProxyInstance(ctype.getClassLoader(), types, handler);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@

import com.google.common.collect.Sets;
import com.netflix.servo.jmx.JmxMonitorRegistry;
import com.netflix.servo.jmx.ObjectNameMapper;
import com.netflix.servo.monitor.BasicCounter;
import com.netflix.servo.monitor.Monitor;
import com.netflix.servo.monitor.MonitorConfig;

import org.testng.annotations.Test;

import java.lang.management.ManagementFactory;
import java.util.Properties;
import java.util.Set;

import javax.management.ObjectName;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

Expand Down Expand Up @@ -136,4 +142,43 @@ public void testGetRegisteredMonitors() throws Exception {
assertTrue(monitors.contains(m1));
assertTrue(monitors.contains(m2));
}*/

@Test
public void testCustomJmxObjectMapper() {
Properties props = new Properties();
props.put("com.netflix.servo.DefaultMonitorRegistry.jmxMapperClass",
"com.netflix.servo.DefaultMonitorRegistryTest$ChangeDomainMapper");
DefaultMonitorRegistry registry = new DefaultMonitorRegistry(props);
BasicCounter counter = new BasicCounter(
new MonitorConfig.Builder("testCustomJmxObjectMapper").build());
registry.register(counter);
ObjectName expectedName =
new ChangeDomainMapper().createObjectName("com.netflix.servo", counter);
assertEquals(expectedName.getDomain(), "com.netflix.servo.Renamed");
assertTrue(ManagementFactory.getPlatformMBeanServer().isRegistered(expectedName));
}

@Test
public void testInvalidMapperDefaults() {
Properties props = new Properties();
props.put("com.netflix.servo.DefaultMonitorRegistry.jmxMapperClass",
"com.my.invalid.class");
DefaultMonitorRegistry registry = new DefaultMonitorRegistry(props);
BasicCounter counter = new BasicCounter(
new MonitorConfig.Builder("testInvalidMapperDefaults").build());
registry.register(counter);
ObjectName expectedName =
ObjectNameMapper.DEFAULT.createObjectName("com.netflix.servo", counter);
assertEquals(expectedName.getDomain(), "com.netflix.servo");
assertTrue(ManagementFactory.getPlatformMBeanServer().isRegistered(expectedName));
}

public static class ChangeDomainMapper implements ObjectNameMapper {

@Override
public ObjectName createObjectName(String domain, Monitor<?> monitor) {
return ObjectNameMapper.DEFAULT.createObjectName(domain + ".Renamed", monitor);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
*/
package com.netflix.servo.monitor;

import com.google.common.collect.Lists;
import com.netflix.servo.DefaultMonitorRegistry;
import com.netflix.servo.tag.BasicTagList;
import com.netflix.servo.tag.TagList;

import org.testng.annotations.Test;

import java.util.List;
Expand All @@ -34,6 +36,10 @@ private interface IDummy {
boolean method2(int n);
Object method3(Object a, Object b);
}

private interface IDummyExtended extends IDummy {
void method4();
}

private static class DummyImpl implements IDummy {
private void sleep(int ms) {
Expand Down Expand Up @@ -61,11 +67,24 @@ public Object method3(Object a, Object b) {
return a;
}
}

private static class ExtendedDummy extends DummyImpl implements IDummyExtended {

@Override
public void method4() {
// do nothing
}

}

@SuppressWarnings("unchecked")
@Test
public void testTimedInterface() {
final IDummy dummy = TimedInterface.newProxy(IDummy.class, new DummyImpl(), "id");
final CompositeMonitor<Long> compositeMonitor = (CompositeMonitor<Long>) dummy;
final List<Monitor<?>> monitors = compositeMonitor.getMonitors();
assertEquals(monitors.size(), 3);
assertEquals(compositeMonitor.getValue().longValue(), 3L);

// you'd register the CompositeMonitor as:
DefaultMonitorRegistry.getInstance().register((CompositeMonitor) dummy);
Expand All @@ -77,11 +96,6 @@ public void testTimedInterface() {
}
}

final CompositeMonitor<Long> compositeMonitor = (CompositeMonitor<Long>) dummy;
final List<Monitor<?>> monitors = compositeMonitor.getMonitors();
assertEquals(monitors.size(), 2);
assertEquals(compositeMonitor.getValue().longValue(), 2L);

final TagList tagList = BasicTagList.of(
TimedInterface.CLASS_TAG, "DummyImpl",
TimedInterface.INTERFACE_TAG, "IDummy",
Expand All @@ -101,12 +115,15 @@ public void testTimedInterface() {
// slow machines
long value = ((Monitor<Long>) monitor).getValue() - 20;
assertTrue(value >= 0 && value <= 9);
} else {
assertEquals(method, "method2");
} else if (method.equals("method2")) {
// expected result is 40, but let's give it a fudge factor to account for
// slow machines
long value = ((Monitor<Long>) monitor).getValue() - 40;
assertTrue(value >= 0 && value <= 9);
} else {
assertEquals(method, "method3");
long value = ((Monitor<Long>) monitor).getValue();
assertEquals(value, 0);
}
}
}
Expand All @@ -127,4 +144,20 @@ public void testTimedInterfaceNoId() {
.withTags(tagList).build();
assertEquals(compositeMonitor.getConfig(), expectedConfig);
}

@SuppressWarnings("unchecked")
@Test
public void testInterfaceInheritence() {
final List<String> expectedNames = Lists.newArrayList("method1", "method2", "method3", "method4");
final IDummyExtended extendedDummy = TimedInterface.newProxy(IDummyExtended.class, new ExtendedDummy());
final CompositeMonitor<Long> compositeMonitor = (CompositeMonitor<Long>) extendedDummy;
DefaultMonitorRegistry.getInstance().register(compositeMonitor);
assertEquals(compositeMonitor.getMonitors().size(), 4);
assertEquals(compositeMonitor.getValue().longValue(), 4L);
assertEquals(compositeMonitor.getValue(100).longValue(), 4L);
final List<Monitor<?>> monitors = compositeMonitor.getMonitors();
for (Monitor<?> monitor : monitors) {
assertTrue(expectedNames.contains(monitor.getConfig().getName()));
}
}
}

0 comments on commit 377a8ca

Please sign in to comment.