Skip to content

Commit 229d434

Browse files
authored
Merge pull request Netflix#441 from dmuino/dynamic-gauge
Dynamic gauge that auto-expires
2 parents 176b7fd + 0a4e44b commit 229d434

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Copyright 2013-2018 Netflix, Inc.
3+
* <p/>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p/>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p/>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.netflix.servo.monitor;
17+
18+
import com.google.common.base.Throwables;
19+
import com.google.common.cache.CacheBuilder;
20+
import com.google.common.cache.CacheLoader;
21+
import com.google.common.cache.LoadingCache;
22+
import com.google.common.collect.ImmutableList;
23+
import com.netflix.servo.DefaultMonitorRegistry;
24+
import com.netflix.servo.tag.TagList;
25+
import org.slf4j.Logger;
26+
import org.slf4j.LoggerFactory;
27+
28+
import java.util.List;
29+
import java.util.concurrent.ConcurrentMap;
30+
import java.util.concurrent.ExecutionException;
31+
import java.util.concurrent.TimeUnit;
32+
33+
/**
34+
* Utility class that dynamically creates gauges based on an arbitrary (name, tagList),
35+
* or {@link com.netflix.servo.monitor.MonitorConfig}
36+
* Gauges are automatically expired after 15 minutes of inactivity.
37+
*/
38+
public final class DynamicGauge implements CompositeMonitor<Long> {
39+
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicGauge.class);
40+
private static final String DEFAULT_EXPIRATION = "15";
41+
private static final String DEFAULT_EXPIRATION_UNIT = "MINUTES";
42+
private static final String CLASS_NAME = DynamicGauge.class.getCanonicalName();
43+
private static final String EXPIRATION_PROP = CLASS_NAME + ".expiration";
44+
private static final String EXPIRATION_PROP_UNIT = CLASS_NAME + ".expirationUnit";
45+
private static final String INTERNAL_ID = "servoGauges";
46+
private static final String CACHE_MONITOR_ID = "servoGaugesCache";
47+
private static final MonitorConfig BASE_CONFIG = new MonitorConfig.Builder(INTERNAL_ID).build();
48+
49+
private static final DynamicGauge INSTANCE = new DynamicGauge();
50+
51+
private final LoadingCache<MonitorConfig, DoubleGauge> gauges;
52+
private final CompositeMonitor<?> cacheMonitor;
53+
54+
private DynamicGauge() {
55+
final String expiration = System.getProperty(EXPIRATION_PROP, DEFAULT_EXPIRATION);
56+
final String expirationUnit = System.getProperty(EXPIRATION_PROP_UNIT, DEFAULT_EXPIRATION_UNIT);
57+
final long expirationValue = Long.parseLong(expiration);
58+
final TimeUnit expirationUnitValue = TimeUnit.valueOf(expirationUnit);
59+
60+
gauges = CacheBuilder.newBuilder()
61+
.expireAfterAccess(expirationValue, expirationUnitValue)
62+
.build(new CacheLoader<MonitorConfig, DoubleGauge>() {
63+
@Override
64+
public DoubleGauge load(final MonitorConfig config) throws Exception {
65+
return new DoubleGauge(config);
66+
}
67+
});
68+
cacheMonitor = Monitors.newCacheMonitor(CACHE_MONITOR_ID, gauges);
69+
DefaultMonitorRegistry.getInstance().register(this);
70+
}
71+
72+
/**
73+
* Set a gauge based on a given {@link MonitorConfig} by a given value.
74+
*
75+
* @param config The monitoring config
76+
* @param value The amount added to the current value
77+
*/
78+
public static void set(MonitorConfig config, double value) {
79+
INSTANCE.get(config).set(value);
80+
}
81+
82+
/**
83+
* Increment a gauge specified by a name.
84+
*/
85+
public static void set(String name, double value) {
86+
set(MonitorConfig.builder(name).build(), value);
87+
}
88+
89+
/**
90+
* Set the gauge for a given name, tagList by a given value.
91+
*/
92+
public static void set(String name, TagList list, double value) {
93+
final MonitorConfig config = MonitorConfig.builder(name).withTags(list).build();
94+
set(config, value);
95+
}
96+
97+
private DoubleGauge get(MonitorConfig config) {
98+
try {
99+
return gauges.get(config);
100+
} catch (ExecutionException e) {
101+
LOGGER.error("Failed to get a gauge for {}: {}", config, e.getMessage());
102+
throw Throwables.propagate(e);
103+
}
104+
}
105+
106+
/**
107+
* {@inheritDoc}
108+
*/
109+
@Override
110+
public List<Monitor<?>> getMonitors() {
111+
final ConcurrentMap<MonitorConfig, DoubleGauge> gaugesMap = gauges.asMap();
112+
return ImmutableList.copyOf(gaugesMap.values());
113+
}
114+
115+
/**
116+
* {@inheritDoc}
117+
*/
118+
@Override
119+
public Long getValue() {
120+
return (long) gauges.asMap().size();
121+
}
122+
123+
@Override
124+
public Long getValue(int pollerIndex) {
125+
return getValue();
126+
}
127+
128+
/**
129+
* {@inheritDoc}
130+
*/
131+
@Override
132+
public MonitorConfig getConfig() {
133+
return BASE_CONFIG;
134+
}
135+
136+
@Override
137+
public String toString() {
138+
ConcurrentMap<MonitorConfig, DoubleGauge> map = gauges.asMap();
139+
return "DynamicGauge{"
140+
+ "baseConfig=" + BASE_CONFIG
141+
+ ", totalGauges=" + map.size()
142+
+ ", gauges=" + map + '}';
143+
}
144+
}

0 commit comments

Comments
 (0)