Skip to content

Commit a8756cc

Browse files
authored
Enable configuration of monitoring sinks. (#219)
* Enable configuration of monitoring sinks. * Addressed PR feedback. * Restored Collectd record TLB.
1 parent c42398b commit a8756cc

File tree

10 files changed

+363
-133
lines changed

10 files changed

+363
-133
lines changed

config/config.conf

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,30 @@ httpPort=7090
1313
# Metrics
1414
# ~~~~
1515
monitoringCluster="mad"
16+
#monitoringService="mad"
17+
#monitoringSinks=[
18+
# {
19+
# class = "com.arpnetworking.metrics.impl.ApacheHttpSink"
20+
# bufferSize = 10000
21+
# uri = "http://localhost:7090/metrics/v3/application"
22+
# parallelism = 2
23+
# maxBatchSize = 500
24+
# emptyQueueInterval = "PT0.5S"
25+
# eventsDroppedLoggingInterval = "PT1M"
26+
# dispatchErrorLoggingInterval = "PT1M"
27+
# unsupportedDataLoggingInterval = "PT1M"
28+
# }
29+
#]
1630
#jvmMetricsCollectionInterval="PT1.0S"
1731

32+
# NOTES:
33+
# - metricsClientHost and metricsClientPort are deprecated
34+
# - metricsClientHost and metricsClientPort no longer default from httpHost and httpPort
35+
# - specifying metricsClientHost or metricsClientPort disables monitoringSinks
36+
# - for backwards compatibility the legacy fields metricsClientHost and metricsClientPort
37+
# take precedence over monitoringSinks
38+
# - migrate to monitoringSinks as soon as possible
39+
1840
# Logging
1941
# ~~~~
2042
logDirectory="logs"

jdk-wrapper.sh

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
# For documentation please refer to:
1818
# https://github.com/KoskiLabs/jdk-wrapper/blob/master/README.md
1919

20+
HTTP_PROTOCOL="http"
21+
FILE_PROTOCOL="file"
22+
23+
LATEST_RELEASE="latest"
24+
SNAPSHOT_RELEASE="snapshot"
25+
2026
log_err() {
2127
l_prefix=$(date +'%H:%M:%S')
2228
printf "[%s] %s\\n" "${l_prefix}" "$@" 1>&2;
@@ -42,37 +48,63 @@ safe_command() {
4248

4349
checksum() {
4450
l_file="$1"
45-
checksum_exec=""
46-
checksum_args=""
51+
l_checksum_exec=""
52+
l_checksum_args=""
4753
if command -v sha256sum > /dev/null; then
48-
checksum_exec="sha256sum"
54+
l_checksum_exec="sha256sum"
4955
elif command -v shasum > /dev/null; then
50-
checksum_exec="shasum"
51-
checksum_args="-a 256"
56+
l_checksum_exec="shasum"
57+
l_checksum_args="-a 256"
5258
elif command -v sha1sum > /dev/null; then
53-
checksum_exec="sha1sum"
59+
l_checksum_exec="sha1sum"
5460
elif command -v md5 > /dev/null; then
55-
checksum_exec="md5"
61+
l_checksum_exec="md5"
5662
fi
57-
if [ -z "${checksum_exec}" ]; then
63+
if [ -z "${l_checksum_exec}" ]; then
5864
log_err "ERROR: No supported checksum command found!"
5965
exit 1
6066
fi
61-
${checksum_exec} ${checksum_args} < "${l_file}"
67+
${l_checksum_exec} ${l_checksum_args} < "${l_file}"
6268
}
6369

6470
rand() {
6571
awk 'BEGIN {srand();printf "%d\n", (rand() * 10^8);}'
6672
}
6773

68-
download_if_needed() {
69-
file="$1"
70-
path="$2"
71-
if [ ! -f "${path}/${file}" ]; then
72-
jdkw_url="${JDKW_BASE_URI}/releases/download/${JDKW_RELEASE}/${file}"
73-
log_out "Downloading ${file} from ${jdkw_url}"
74-
safe_command "curl ${curl_options} -f -k -L -o \"${path}/${file}\" \"${jdkw_url}\""
75-
safe_command "chmod +x \"${path}/${file}\""
74+
get_protocol() {
75+
case "${JDKW_BASE_URI}" in
76+
http://*|https://*)
77+
printf "%s" "${HTTP_PROTOCOL}"
78+
;;
79+
file://*)
80+
printf "%s" "${FILE_PROTOCOL}"
81+
;;
82+
*)
83+
log_err "ERROR: Unsupported protocol in JDKW_BASE_URI: ${JDKW_BASE_URI}"
84+
exit 1
85+
esac
86+
}
87+
88+
obtain_if_needed() {
89+
l_file="$1"
90+
l_target_path="$2"
91+
if [ ! -f "${l_target_path}/${l_file}" ]; then
92+
case "${JDKW_BASE_URI}" in
93+
http://*|https://*)
94+
l_jdkw_url="${JDKW_BASE_URI}/releases/download/${JDKW_RELEASE}/${l_file}"
95+
log_out "Downloading ${l_file} from ${l_jdkw_url}"
96+
safe_command "curl ${curl_options} -f -k -L -o \"${l_target_path}/${l_file}\" \"${l_jdkw_url}\""
97+
;;
98+
file://*)
99+
l_jdkw_path="${JDKW_BASE_URI#file://}/${l_file}"
100+
log_out "Copying ${l_file} from ${l_jdkw_path}"
101+
safe_command "cp \"${l_jdkw_path}\" \"${l_target_path}/${l_file}\""
102+
;;
103+
*)
104+
log_err "ERROR: Unsupported protocol in JDKW_BASE_URI: ${JDKW_BASE_URI}"
105+
exit 1
106+
esac
107+
safe_command "chmod +x \"${l_target_path}/${l_file}\""
76108
fi
77109
}
78110

@@ -98,7 +130,6 @@ done < "${l_fifo}"
98130
safe_command "rm \"${l_fifo}\""
99131

100132
# Process (but do not load) properties from command line arguments
101-
command=
102133
cmd_configuration=
103134
for arg in "$@"; do
104135
jdkw_arg=$(echo "${arg}" | grep '^JDKW_.*')
@@ -109,13 +140,6 @@ for arg in "$@"; do
109140
if [ -n "${jdkw_arg}" ]; then
110141
cmd_configuration="${cmd_configuration}${arg} "
111142
fi
112-
case "${arg}" in
113-
*\'*)
114-
arg=$(printf "%s" "$arg" | sed "s/'/'\"'\"'/g")
115-
;;
116-
*) : ;;
117-
esac
118-
command="${command} '${arg}'"
119143
done
120144

121145
# Default base directory to current working directory
@@ -144,7 +168,10 @@ if [ -z "${JDKW_BASE_URI}" ]; then
144168
JDKW_BASE_URI="https://github.com/KoskiLabs/jdk-wrapper"
145169
fi
146170
if [ -z "${JDKW_RELEASE}" ]; then
147-
JDKW_RELEASE="latest"
171+
JDKW_RELEASE="${LATEST_RELEASE}"
172+
if [ $(get_protocol) = "${FILE_PROTOCOL}" ]; then
173+
JDKW_RELEASE="${SNAPSHOT_RELEASE}"
174+
fi
148175
log_out "Defaulted to version ${JDKW_RELEASE}"
149176
fi
150177
if [ -z "${JDKW_TARGET}" ]; then
@@ -156,7 +183,7 @@ if [ -z "${JDKW_VERBOSE}" ]; then
156183
fi
157184

158185
# Resolve latest version
159-
if [ "${JDKW_RELEASE}" = "latest" ]; then
186+
if [ "${JDKW_RELEASE}" = "${LATEST_RELEASE}" ]; then
160187
latest_version_json="${TMPDIR:-/tmp}/jdkw-latest-version-$$.$(rand)"
161188
safe_command "curl ${curl_options} -f -k -L -o \"${latest_version_json}\" -H 'Accept: application/json' \"${JDKW_BASE_URI}/releases/latest\""
162189
JDKW_RELEASE=$(sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/' < "${latest_version_json}")
@@ -166,6 +193,10 @@ fi
166193

167194
# Ensure target directory exists
168195
jdkw_path="${JDKW_TARGET}/jdkw/${JDKW_RELEASE}"
196+
if [ -d "${jdkw_path}" ] && [ "${JDKW_RELEASE}" = "${SNAPSHOT_RELEASE}" ]; then
197+
log_out "Removing target snapshot directory ${jdkw_path}"
198+
safe_command "rm -rf \"${jdkw_path}\""
199+
fi
169200
if [ ! -d "${jdkw_path}" ]; then
170201
log_out "Creating target directory ${jdkw_path}"
171202
safe_command "mkdir -p \"${jdkw_path}\""
@@ -174,8 +205,8 @@ fi
174205
# Download the jdk wrapper version
175206
jdkw_impl="jdkw-impl.sh"
176207
jdkw_wrapper="jdk-wrapper.sh"
177-
download_if_needed "${jdkw_impl}" "${jdkw_path}"
178-
download_if_needed "${jdkw_wrapper}" "${jdkw_path}"
208+
obtain_if_needed "${jdkw_impl}" "${jdkw_path}"
209+
obtain_if_needed "${jdkw_wrapper}" "${jdkw_path}"
179210

180211
# Check whether this wrapper is the one specified for this version
181212
jdkw_download="${jdkw_path}/${jdkw_wrapper}"

pom.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@
9292
<log4j.over.slf4j.version>1.7.25</log4j.over.slf4j.version>
9393
<metrics.aggregator.protocol.prometheus.version>1.0.0</metrics.aggregator.protocol.prometheus.version>
9494
<metrics.aggregator.protocol.version>1.0.10</metrics.aggregator.protocol.version>
95-
<metrics.client.version>0.11.1</metrics.client.version>
96-
<metrics.client.http.version>0.11.1</metrics.client.http.version>
97-
<metrics.client.incubator.version>0.11.0</metrics.client.incubator.version>
98-
<metrics.jvm.extra.version>0.11.0</metrics.jvm.extra.version>
95+
<metrics.client.version>0.11.2</metrics.client.version>
96+
<metrics.client.http.version>0.11.2</metrics.client.http.version>
97+
<metrics.client.incubator.version>0.11.2</metrics.client.incubator.version>
98+
<metrics.jvm.extra.version>0.11.2</metrics.jvm.extra.version>
9999
<oval.version>1.90</oval.version>
100100
<protobuf.version>3.8.0</protobuf.version>
101101
<scala.version>2.11</scala.version>

src/main/java/com/arpnetworking/metrics/mad/Main.java

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import com.arpnetworking.utility.Configurator;
5858
import com.arpnetworking.utility.Launchable;
5959
import com.arpnetworking.utility.ScalaForkJoinPoolAdapter;
60+
import com.fasterxml.jackson.databind.JsonNode;
6061
import com.fasterxml.jackson.databind.ObjectMapper;
6162
import com.google.common.collect.ImmutableList;
6263
import com.google.common.collect.Maps;
@@ -75,7 +76,7 @@
7576

7677
import java.io.File;
7778
import java.net.URI;
78-
import java.util.Collections;
79+
import java.util.ArrayList;
7980
import java.util.List;
8081
import java.util.Locale;
8182
import java.util.Map;
@@ -88,6 +89,7 @@
8889
import java.util.concurrent.ScheduledExecutorService;
8990
import java.util.concurrent.Semaphore;
9091
import java.util.concurrent.TimeUnit;
92+
import javax.annotation.Nullable;
9193

9294
/**
9395
* Class containing entry point for Metrics Data Aggregator (MAD).
@@ -264,6 +266,7 @@ private void launchActors(final Injector injector) {
264266
.run(materializer);
265267
}
266268

269+
@SuppressWarnings("deprecation")
267270
private Injector launchGuice(final ActorSystem actorSystem) {
268271
LOGGER.info().setMessage("Launching guice").log();
269272

@@ -282,17 +285,28 @@ private Injector launchGuice(final ActorSystem actorSystem) {
282285
}
283286

284287
// Instantiate the metrics factory
285-
final URI sinkUrl = URI.create(
286-
"http://" + _configuration.getMetricsClientHost() + ":"
287-
+ _configuration.getMetricsClientPort() + "/metrics/v3/application");
288+
final ImmutableList.Builder<com.arpnetworking.metrics.Sink> monitoringSinksBuilder =
289+
new ImmutableList.Builder<>();
290+
if (_configuration.getMetricsClientHost().isPresent()
291+
|| _configuration.getMetricsClientPort().isPresent()) {
292+
final String endpoint = String.format(
293+
"http://%s:%d/metrics/v3/application",
294+
_configuration.getMetricsClientHost().orElse("localhost"),
295+
_configuration.getMetricsClientPort().orElse(7090));
296+
297+
monitoringSinksBuilder.add(
298+
new ApacheHttpSink.Builder()
299+
.setUri(URI.create(endpoint))
300+
.build());
301+
302+
} else {
303+
monitoringSinksBuilder.addAll(createSinks(_configuration.getMonitoringSinks()));
304+
}
305+
288306
final MetricsFactory metricsFactory = new TsdMetricsFactory.Builder()
289307
.setClusterName(_configuration.getMonitoringCluster())
290308
.setServiceName(_configuration.getMonitoringService())
291-
.setSinks(
292-
Collections.singletonList(
293-
new ApacheHttpSink.Builder()
294-
.setUri(sinkUrl)
295-
.build()))
309+
.setSinks(monitoringSinksBuilder.build())
296310
.build();
297311

298312
final AppShutdown shutdown = new AppShutdown();
@@ -302,6 +316,36 @@ private Injector launchGuice(final ActorSystem actorSystem) {
302316
return Guice.createInjector(new MainModule(actorSystem, metricsFactory, shutdown));
303317
}
304318

319+
@SuppressFBWarnings("REC_CATCH_EXCEPTION")
320+
static List<com.arpnetworking.metrics.Sink> createSinks(
321+
final ImmutableList<JsonNode> monitoringSinks) {
322+
// Until we implement the Commons Builder pattern in the metrics client
323+
// library we need to resort to a more brute-force deserialization
324+
// style. The benefit of this approach is that it will be forwards
325+
// compatible with the Commons Builder approach. The drawbacks are
326+
// the ugly way the configuration is passed around (as JsonNode) and
327+
// then two-step deserialized.
328+
final List<com.arpnetworking.metrics.Sink> sinks = new ArrayList<>();
329+
for (final JsonNode sinkNode : monitoringSinks) {
330+
@Nullable final JsonNode classNode = sinkNode.get("class");
331+
try {
332+
if (classNode != null) {
333+
final Class<?> builderClass = Class.forName(classNode.textValue() + "$Builder");
334+
final Object builder = OBJECT_MAPPER.treeToValue(sinkNode, builderClass);
335+
@SuppressWarnings("unchecked")
336+
final com.arpnetworking.metrics.Sink sink =
337+
(com.arpnetworking.metrics.Sink) builderClass.getMethod("build").invoke(builder);
338+
sinks.add(sink);
339+
}
340+
// CHECKSTYLE.OFF: IllegalCatch - There are so many ways this hack can fail!
341+
} catch (final Exception e) {
342+
// CHECKSTYLE.ON: IllegalCatch
343+
throw new RuntimeException("Unable to create sink from: " + sinkNode.toString(), e);
344+
}
345+
}
346+
return sinks;
347+
}
348+
305349
private ActorSystem launchAkka() {
306350
final Config akkaConfiguration = ConfigFactory.parseMap(_configuration.getAkkaConfiguration());
307351
return ActorSystem.create("MAD", ConfigFactory.load(akkaConfiguration));

0 commit comments

Comments
 (0)