Skip to content

Commit 8bfce17

Browse files
author
Vladimir Kotal
committed
use statsd for indexer monitoring (#3279)
introduce StatsdRegistry Co-authored-by: Adam Hornáček <[email protected]> fixes #3245
1 parent 8415cdc commit 8bfce17

File tree

14 files changed

+323
-40
lines changed

14 files changed

+323
-40
lines changed

opengrok-indexer/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,11 @@ Portions Copyright (c) 2020-2020, Lubos Kosco <[email protected]>.
210210
<artifactId>micrometer-registry-prometheus</artifactId>
211211
<version>${micrometer.version}</version>
212212
</dependency>
213+
<dependency>
214+
<groupId>io.micrometer</groupId>
215+
<artifactId>micrometer-registry-statsd</artifactId>
216+
<version>${micrometer.version}</version>
217+
</dependency>
213218
</dependencies>
214219

215220
<build>

opengrok-indexer/src/main/java/org/opengrok/indexer/Metrics.java

+98-8
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,121 @@
2222
*/
2323
package org.opengrok.indexer;
2424

25+
import io.micrometer.core.instrument.Clock;
26+
import io.micrometer.core.instrument.MeterRegistry;
27+
import io.micrometer.core.instrument.Tag;
2528
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics;
2629
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
2730
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
2831
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
2932
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
3033
import io.micrometer.prometheus.PrometheusConfig;
3134
import io.micrometer.prometheus.PrometheusMeterRegistry;
35+
import io.micrometer.statsd.StatsdConfig;
36+
import io.micrometer.statsd.StatsdMeterRegistry;
37+
import io.micrometer.statsd.StatsdFlavor;
38+
import org.opengrok.indexer.configuration.RuntimeEnvironment;
39+
import org.opengrok.indexer.index.Indexer;
40+
import org.opengrok.indexer.logger.LoggerFactory;
3241

42+
import java.util.Collections;
43+
import java.util.List;
44+
import java.util.logging.Level;
45+
import java.util.logging.Logger;
46+
import java.util.stream.Collectors;
47+
48+
/**
49+
* Encapsulates logic of meter registry setup and handling.
50+
* Generally, the web application publishes metrics to Prometheus and the Indexer to StatsD.
51+
*/
3352
public final class Metrics {
3453

35-
private static final PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
54+
private static final Logger LOGGER = LoggerFactory.getLogger(Metrics.class);
55+
56+
private static final StatsdConfig statsdConfig = new StatsdConfig() {
57+
@Override
58+
public String get(String k) {
59+
return null;
60+
}
61+
62+
@Override
63+
public StatsdFlavor flavor() {
64+
return RuntimeEnvironment.getInstance().getStatsdConfig().getFlavor();
65+
}
66+
67+
@Override
68+
public int port() {
69+
return RuntimeEnvironment.getInstance().getStatsdConfig().getPort();
70+
}
71+
72+
@Override
73+
public String host() {
74+
return RuntimeEnvironment.getInstance().getStatsdConfig().getHost();
75+
}
76+
77+
@Override
78+
public boolean buffered() {
79+
return true;
80+
}
81+
};
82+
83+
private static PrometheusMeterRegistry prometheusRegistry;
84+
private static StatsdMeterRegistry statsdRegistry;
3685

3786
static {
38-
new ClassLoaderMetrics().bindTo(registry);
39-
new JvmMemoryMetrics().bindTo(registry);
40-
new JvmGcMetrics().bindTo(registry);
41-
new ProcessorMetrics().bindTo(registry);
42-
new JvmThreadMetrics().bindTo(registry);
87+
MeterRegistry registry = null;
88+
89+
if (RuntimeEnvironment.getInstance().getStatsdConfig().isEnabled()) {
90+
LOGGER.log(Level.INFO, "configuring StatsdRegistry");
91+
statsdRegistry = new StatsdMeterRegistry(statsdConfig, Clock.SYSTEM);
92+
registry = statsdRegistry;
93+
} else if (!RuntimeEnvironment.getInstance().isIndexer()) {
94+
LOGGER.log(Level.INFO, "configuring PrometheusRegistry");
95+
prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
96+
registry = prometheusRegistry;
97+
}
98+
99+
if (registry != null) {
100+
new ClassLoaderMetrics().bindTo(registry);
101+
new JvmMemoryMetrics().bindTo(registry);
102+
new JvmGcMetrics().bindTo(registry);
103+
new ProcessorMetrics().bindTo(registry);
104+
new JvmThreadMetrics().bindTo(registry);
105+
}
43106
}
44107

45108
private Metrics() {
46109
}
47110

48-
public static PrometheusMeterRegistry getRegistry() {
49-
return registry;
111+
public static void updateSubFiles(List<String> subFiles) {
112+
// Add tag for per-project reindex.
113+
if (statsdRegistry != null && !subFiles.isEmpty()) {
114+
String projects = subFiles.stream().
115+
map(s -> s.startsWith(Indexer.PATH_SEPARATOR_STRING) ? s.substring(1) : s).
116+
collect(Collectors.joining(","));
117+
Tag commonTag = Tag.of("projects", projects);
118+
LOGGER.log(Level.FINE, "updating statsdRegistry with common tag: {}", commonTag);
119+
statsdRegistry.config().commonTags(Collections.singleton(commonTag));
120+
}
121+
}
122+
123+
public static PrometheusMeterRegistry getPrometheusRegistry() {
124+
return prometheusRegistry;
50125
}
51126

127+
private static StatsdMeterRegistry getStatsdRegistry() {
128+
return statsdRegistry;
129+
}
130+
131+
/**
132+
* Get registry based on running context.
133+
* @return MeterRegistry instance
134+
*/
135+
public static MeterRegistry getRegistry() {
136+
if (RuntimeEnvironment.getInstance().isIndexer()) {
137+
return getStatsdRegistry();
138+
} else {
139+
return getPrometheusRegistry();
140+
}
141+
}
52142
}

opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Configuration.java

+13
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ public final class Configuration {
299299

300300
private SuggesterConfig suggesterConfig = new SuggesterConfig();
301301

302+
private StatsdConfig statsdConfig = new StatsdConfig();
303+
302304
private Set<String> disabledRepositories;
303305

304306
/*
@@ -1315,6 +1317,17 @@ public void setSuggesterConfig(final SuggesterConfig config) {
13151317
this.suggesterConfig = config;
13161318
}
13171319

1320+
public StatsdConfig getStatsdConfig() {
1321+
return statsdConfig;
1322+
}
1323+
1324+
public void setStatsdConfig(final StatsdConfig config) {
1325+
if (config == null) {
1326+
throw new IllegalArgumentException("Cannot set Statsd configuration to null");
1327+
}
1328+
this.statsdConfig = config;
1329+
}
1330+
13181331
public Set<String> getDisabledRepositories() {
13191332
return disabledRepositories;
13201333
}

opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/ConfigurationHelp.java

+2
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ private static Object getSampleValue(Method setter, Object defaultValue) {
183183
return null;
184184
} else if (paramType == SuggesterConfig.class) {
185185
return SuggesterConfig.getForHelp();
186+
} else if (paramType == StatsdConfig.class) {
187+
return StatsdConfig.getForHelp();
186188
} else {
187189
throw new UnsupportedOperationException("getSampleValue() for " +
188190
paramType + ", " + genType);

opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/RuntimeEnvironment.java

+24
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ public final class RuntimeEnvironment {
123123

124124
public WatchDogService watchDog;
125125

126+
public List<String> getSubFiles() {
127+
return subFiles;
128+
}
129+
130+
private List<String> subFiles = new ArrayList<>();
131+
126132
/**
127133
* Creates a new instance of RuntimeEnvironment. Private to ensure a
128134
* singleton anti-pattern.
@@ -141,6 +147,16 @@ private RuntimeEnvironment() {
141147
private AuthorizationFramework authFramework;
142148
private final Object authFrameworkLock = new Object();
143149

150+
private boolean indexer;
151+
152+
public boolean isIndexer() {
153+
return indexer;
154+
}
155+
156+
public void setIndexer(boolean indexer) {
157+
this.indexer = indexer;
158+
}
159+
144160
/** Gets the thread pool used for multi-project searches. */
145161
public ExecutorService getSearchExecutor() {
146162
return lzSearchExecutor.get();
@@ -1876,6 +1892,14 @@ public void setSuggesterConfig(SuggesterConfig suggesterConfig) {
18761892
syncWriteConfiguration(suggesterConfig, Configuration::setSuggesterConfig);
18771893
}
18781894

1895+
public StatsdConfig getStatsdConfig() {
1896+
return syncReadConfiguration(Configuration::getStatsdConfig);
1897+
}
1898+
1899+
public void setStatsdConfig(StatsdConfig statsdConfig) {
1900+
syncWriteConfiguration(statsdConfig, Configuration::setStatsdConfig);
1901+
}
1902+
18791903
/**
18801904
* Applies the specified function to the runtime configuration, after having
18811905
* obtained the configuration read-lock (and releasing afterward).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* CDDL HEADER START
3+
*
4+
* The contents of this file are subject to the terms of the
5+
* Common Development and Distribution License (the "License").
6+
* You may not use this file except in compliance with the License.
7+
*
8+
* See LICENSE.txt included in this distribution for the specific
9+
* language governing permissions and limitations under the License.
10+
*
11+
* When distributing Covered Code, include this CDDL HEADER in each
12+
* file and include the License file at LICENSE.txt.
13+
* If applicable, add the following below this CDDL HEADER, with the
14+
* fields enclosed by brackets "[]" replaced with your own identifying
15+
* information: Portions Copyright [yyyy] [name of copyright owner]
16+
*
17+
* CDDL HEADER END
18+
*/
19+
20+
/*
21+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
22+
*/
23+
24+
package org.opengrok.indexer.configuration;
25+
26+
import io.micrometer.statsd.StatsdFlavor;
27+
28+
/**
29+
* Configuration for Statsd metrics emitted by the Indexer via {@link org.opengrok.indexer.util.Statistics}.
30+
*/
31+
public class StatsdConfig {
32+
private int port;
33+
private String host;
34+
private boolean enabled;
35+
private StatsdFlavor flavor;
36+
37+
public String getHost() {
38+
return host;
39+
}
40+
41+
public void setHost(String host) {
42+
this.host = host;
43+
}
44+
45+
public int getPort() {
46+
return port;
47+
}
48+
49+
public void setPort(int port) {
50+
this.port = port;
51+
}
52+
53+
public StatsdFlavor getFlavor() {
54+
return flavor;
55+
}
56+
57+
public void setFlavor(StatsdFlavor flavor) {
58+
this.flavor = flavor;
59+
}
60+
61+
public boolean isEnabled() {
62+
return port != 0 && host != null && !host.isEmpty() && flavor != null;
63+
}
64+
65+
/**
66+
* Gets an instance version suitable for helper documentation by shifting
67+
* most default properties slightly.
68+
*/
69+
static StatsdConfig getForHelp() {
70+
StatsdConfig res = new StatsdConfig();
71+
res.setHost("foo.bar");
72+
res.setPort(8125);
73+
res.setFlavor(StatsdFlavor.ETSY);
74+
return res;
75+
}
76+
}

opengrok-indexer/src/main/java/org/opengrok/indexer/history/HistoryGuru.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ public void run() {
618618
LOGGER.log(Level.WARNING,
619619
"Failed optimizing the history cache database", he);
620620
}
621-
elapsed.report(LOGGER, "Done historycache for all repositories");
621+
elapsed.report(LOGGER, "Done history cache for all repositories", "indexer.history.cache");
622622
historyCache.setHistoryIndexDone();
623623
}
624624

@@ -884,7 +884,8 @@ public void run() {
884884
repositories.clear();
885885
newrepos.forEach((_key, repo) -> putRepository(repo));
886886

887-
elapsed.report(LOGGER, String.format("done invalidating %d repositories", newrepos.size()));
887+
elapsed.report(LOGGER, String.format("done invalidating %d repositories", newrepos.size()),
888+
"history.repositories.invalidate");
888889
}
889890

890891
/**

opengrok-indexer/src/main/java/org/opengrok/indexer/index/Indexer.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@
5050
import java.util.logging.Level;
5151
import java.util.logging.Logger;
5252
import java.util.stream.Collectors;
53+
5354
import org.opengrok.indexer.Info;
55+
import org.opengrok.indexer.Metrics;
5456
import org.opengrok.indexer.analysis.AnalyzerGuru;
5557
import org.opengrok.indexer.analysis.AnalyzerGuruHelp;
5658
import org.opengrok.indexer.analysis.Ctags;
@@ -148,7 +150,7 @@ public static void main(String[] argv) {
148150
boolean update = true;
149151

150152
Executor.registerErrorHandler();
151-
ArrayList<String> subFiles = new ArrayList<>();
153+
List<String> subFiles = RuntimeEnvironment.getInstance().getSubFiles();
152154
ArrayList<String> subFilesList = new ArrayList<>();
153155

154156
boolean createDict = false;
@@ -177,6 +179,7 @@ public static void main(String[] argv) {
177179
}
178180

179181
env = RuntimeEnvironment.getInstance();
182+
env.setIndexer(true);
180183

181184
// Complete the configuration of repository types.
182185
List<Class<? extends Repository>> repositoryClasses
@@ -316,6 +319,8 @@ public static void main(String[] argv) {
316319
System.exit(1);
317320
}
318321

322+
Metrics.updateSubFiles(subFiles);
323+
319324
// If the webapp is running with a config that does not contain
320325
// 'projectsEnabled' property (case of upgrade or transition
321326
// from project-less config to one with projects), set the property
@@ -385,7 +390,7 @@ public static void main(String[] argv) {
385390
System.err.println("Exception: " + e.getLocalizedMessage());
386391
System.exit(1);
387392
} finally {
388-
stats.report(LOGGER);
393+
stats.report(LOGGER, "Indexer finished", "indexer.total");
389394
}
390395
}
391396

@@ -990,7 +995,7 @@ public void prepareIndexer(RuntimeEnvironment env,
990995
Statistics stats = new Statistics();
991996
env.setRepositories(searchPaths.toArray(new String[0]));
992997
stats.report(LOGGER, String.format("Done scanning for repositories, found %d repositories",
993-
env.getRepositories().size()));
998+
env.getRepositories().size()), "indexer.repository.scan");
994999
}
9951000

9961001
if (createHistoryCache) {
@@ -1102,7 +1107,7 @@ public void run() {
11021107
LOGGER.log(Level.WARNING, "Received interrupt while waiting" +
11031108
" for executor to finish", exp);
11041109
}
1105-
elapsed.report(LOGGER, "Done indexing data of all repositories");
1110+
elapsed.report(LOGGER, "Done indexing data of all repositories", "indexer.repository.indexing");
11061111

11071112
CtagsUtil.deleteTempFiles();
11081113
}

opengrok-indexer/src/main/java/org/opengrok/indexer/util/Executor.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,9 @@ public void run() {
232232

233233
ret = process.waitFor();
234234

235-
stat.report(LOGGER, Level.FINE, String.format("Finished command [%s] in directory %s with exit code %d",
236-
cmd_str, dir_str, ret));
235+
stat.report(LOGGER, Level.FINE,
236+
String.format("Finished command [%s] in directory %s with exit code %d", cmd_str, dir_str, ret),
237+
"executor.latency");
237238
LOGGER.log(Level.FINE,
238239
"Finished command [{0}] in directory {1} with exit code {2}",
239240
new Object[] {cmd_str, dir_str, ret});

0 commit comments

Comments
 (0)