Marvel: Add dedicated settings service

This commit adds a dedicated MarvelSettingsService to centralized all Marvel settings.

Original commit: elastic/x-pack-elasticsearch@f7b97be396
This commit is contained in:
Tanguy Leroux 2015-07-28 18:13:53 +02:00
parent e1c4285482
commit 597d704c17
9 changed files with 523 additions and 31 deletions

View File

@ -8,22 +8,22 @@ package org.elasticsearch.marvel;
import com.google.common.collect.ImmutableList;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.common.inject.Scopes;
import org.elasticsearch.common.inject.SpawnModules;
import org.elasticsearch.marvel.agent.AgentService;
import org.elasticsearch.marvel.agent.collector.CollectorModule;
import org.elasticsearch.marvel.agent.exporter.ExporterModule;
import org.elasticsearch.marvel.agent.settings.MarvelSettingsModule;
import org.elasticsearch.marvel.license.LicenseModule;
public class MarvelModule extends AbstractModule implements SpawnModules {
@Override
protected void configure() {
bind(AgentService.class).in(Scopes.SINGLETON);
bind(AgentService.class).asEagerSingleton();
}
@Override
public Iterable<? extends Module> spawnModules() {
return ImmutableList.of(new LicenseModule(), new CollectorModule(), new ExporterModule());
return ImmutableList.of(new MarvelSettingsModule(), new LicenseModule(), new CollectorModule(), new ExporterModule());
}
}

View File

@ -8,7 +8,6 @@ package org.elasticsearch.marvel.agent;
import com.google.common.collect.ImmutableSet;
import org.elasticsearch.cluster.settings.ClusterDynamicSettings;
import org.elasticsearch.cluster.settings.DynamicSettings;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
@ -28,19 +27,15 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> imple
private static final String SETTINGS_BASE = "marvel.agent.";
public static final String SETTINGS_INTERVAL = SETTINGS_BASE + "interval";
public static final String SETTINGS_INDICES = SETTINGS_BASE + "indices";
public static final String SETTINGS_ENABLED = SETTINGS_BASE + "enabled";
public static final String SETTINGS_STATS_TIMEOUT = SETTINGS_BASE + "stats.timeout";
public static final String SETTINGS_INDICES_STATS_TIMEOUT = SETTINGS_BASE + "stats.indices.timeout";
private volatile ExportingWorker exportingWorker;
private volatile Thread workerThread;
private volatile long samplingInterval;
volatile private String[] indicesToExport = Strings.EMPTY_ARRAY;
private volatile TimeValue indicesStatsTimeout;
private volatile TimeValue clusterStatsTimeout;
private final Collection<Collector> collectors;
@ -53,10 +48,8 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> imple
Set<Collector> collectors, Set<Exporter> exporters) {
super(settings);
this.samplingInterval = settings.getAsTime(SETTINGS_INTERVAL, TimeValue.timeValueSeconds(10)).millis();
this.indicesToExport = settings.getAsArray(SETTINGS_INDICES, this.indicesToExport, true);
TimeValue statsTimeout = settings.getAsTime(SETTINGS_STATS_TIMEOUT, TimeValue.timeValueMinutes(10));
indicesStatsTimeout = settings.getAsTime(SETTINGS_INDICES_STATS_TIMEOUT, statsTimeout);
if (settings.getAsBoolean(SETTINGS_ENABLED, true)) {
this.collectors = ImmutableSet.copyOf(collectors);
@ -69,9 +62,7 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> imple
nodeSettingsService.addListener(this);
dynamicSettings.addDynamicSetting(SETTINGS_INTERVAL);
dynamicSettings.addDynamicSetting(SETTINGS_INDICES + ".*"); // array settings
dynamicSettings.addDynamicSetting(SETTINGS_STATS_TIMEOUT);
dynamicSettings.addDynamicSetting(SETTINGS_INDICES_STATS_TIMEOUT);
logger.trace("marvel is running in [{}] mode", licenseService.mode());
}
@ -163,20 +154,6 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> imple
samplingInterval = newSamplingInterval.millis();
applyIntervalSettings();
}
String[] indices = settings.getAsArray(SETTINGS_INDICES, null, true);
if (indices != null) {
logger.info("sampling indices updated to [{}]", Strings.arrayToCommaDelimitedString(indices));
indicesToExport = indices;
}
TimeValue statsTimeout = settings.getAsTime(SETTINGS_STATS_TIMEOUT, TimeValue.timeValueMinutes(10));
TimeValue newTimeValue = settings.getAsTime(SETTINGS_INDICES_STATS_TIMEOUT, statsTimeout);
if (!indicesStatsTimeout.equals(newTimeValue)) {
logger.info("indices stats timeout updated to [{}]", newTimeValue);
indicesStatsTimeout = newTimeValue;
}
}
class ExportingWorker implements Runnable {

View File

@ -15,12 +15,13 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.marvel.agent.collector.AbstractCollector;
import org.elasticsearch.marvel.agent.exporter.MarvelDoc;
import org.elasticsearch.marvel.agent.settings.MarvelSettingsService;
import java.util.Collection;
/**
* Collector for indices statistics.
*
* <p/>
* This collector runs on the master node only and collect a {@link IndexStatsMarvelDoc} document
* for each existing index in the cluster.
*/
@ -31,12 +32,14 @@ public class IndexStatsCollector extends AbstractCollector<IndexStatsCollector>
private final ClusterName clusterName;
private final Client client;
private final MarvelSettingsService marvelSettings;
@Inject
public IndexStatsCollector(Settings settings, ClusterService clusterService, ClusterName clusterName, Client client) {
public IndexStatsCollector(Settings settings, ClusterService clusterService, ClusterName clusterName, Client client, MarvelSettingsService marvelSettings) {
super(settings, NAME, clusterService);
this.client = client;
this.clusterName = clusterName;
this.marvelSettings = marvelSettings;
}
@Override
@ -48,11 +51,12 @@ public class IndexStatsCollector extends AbstractCollector<IndexStatsCollector>
protected Collection<MarvelDoc> doCollect() throws Exception {
ImmutableList.Builder<MarvelDoc> results = ImmutableList.builder();
IndicesStatsResponse indicesStats = client.admin().indices().prepareStats().all()
IndicesStatsResponse indicesStats = client.admin().indices().prepareStats()
.setStore(true)
.setIndexing(true)
.setDocs(true)
.get();
.setIndices(marvelSettings.indices())
.get(marvelSettings.indexStatsTimeout());
long timestamp = System.currentTimeMillis();
for (IndexStats indexStats : indicesStats.getIndices().values()) {

View File

@ -0,0 +1,209 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.marvel.agent.settings;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
public abstract class MarvelSetting<V> {
private final String name;
private final String description;
private final V defaultValue;
private volatile V value;
MarvelSetting(String name, String description, V defaultValue) {
this.name = name;
this.description = description;
this.defaultValue = defaultValue;
}
abstract boolean onInit(Settings settings);
abstract boolean onRefresh(Settings settings);
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public V getDefaultValue() {
return defaultValue;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public String getValueAsString() {
return getValue() != null ? getValue().toString() : "null";
}
public boolean isDynamic() {
return true;
}
public String dynamicSettingName() {
return getName();
}
public static BooleanSetting booleanSetting(String name, Boolean defaultValue, String description) {
return new BooleanSetting(name, description, defaultValue);
}
public static StringSetting stringSetting(String name, String defaultValue, String description) {
return new StringSetting(name, description, defaultValue);
}
public static StringArraySetting arraySetting(String name, String[] defaultValue, String description) {
return new StringArraySetting(name, description, defaultValue);
}
public static TimeValueSetting timeSetting(String name, TimeValue defaultValue, String description) {
return new TimeValueSetting(name, description, defaultValue);
}
static class BooleanSetting extends MarvelSetting<Boolean> {
BooleanSetting(String name, String description, Boolean defaultValue) {
super(name, description, defaultValue);
}
@Override
boolean onInit(Settings settings) {
setValue(settings.getAsBoolean(getName(), getDefaultValue()));
return true;
}
@Override
boolean onRefresh(Settings settings) {
Boolean updated = settings.getAsBoolean(getName(), null);
if ((updated != null) && !updated.equals(getValue())) {
setValue(updated);
return true;
}
return false;
}
}
static class StringSetting extends MarvelSetting<String> {
StringSetting(String name, String description, String defaultValue) {
super(name, description, defaultValue);
}
@Override
boolean onInit(Settings settings) {
setValue(settings.get(getName(), getDefaultValue()));
return true;
}
@Override
boolean onRefresh(Settings settings) {
String updated = settings.get(getName(), null);
if ((updated != null) && !updated.equals(getValue())) {
setValue(updated);
return true;
}
return false;
}
}
static class StringArraySetting extends MarvelSetting<String[]> {
StringArraySetting(String name, String description, String[] defaultValue) {
super(name, description, defaultValue);
}
@Override
boolean onInit(Settings settings) {
String[] a;
if (getDefaultValue() != null) {
a = settings.getAsArray(getName(), getDefaultValue(), true);
} else {
a = settings.getAsArray(getName());
}
if (a != null) {
setValue(a);
return true;
}
return false;
}
@Override
boolean onRefresh(Settings settings) {
String[] updated = settings.getAsArray(getName(), null);
if (updated != null) {
setValue(updated);
return true;
}
return false;
}
@Override
public String dynamicSettingName() {
// array settings
return super.dynamicSettingName() + ".*";
}
@Override
public String getValueAsString() {
return Strings.arrayToCommaDelimitedString(getValue());
}
}
static class TimeValueSetting extends MarvelSetting<TimeValue> {
TimeValueSetting(String name, String description, TimeValue defaultValue) {
super(name, description, defaultValue);
}
@Override
boolean onInit(Settings settings) {
TimeValue t = get(settings, getDefaultValue());
if (t != null) {
setValue(t);
return true;
}
return false;
}
@Override
boolean onRefresh(Settings settings) {
TimeValue updated = get(settings, null);
if ((updated != null) && ((getValue() == null) || (updated.millis() != getValue().millis()))) {
setValue(updated);
return true;
}
return false;
}
private TimeValue get(Settings settings, TimeValue defaultValue) {
try {
TimeValue t = settings.getAsTime(getName(), defaultValue);
if (t != null) {
return t;
}
} catch (ElasticsearchParseException e) {
Long l = settings.getAsLong(getName(), defaultValue != null ? defaultValue.millis() : null);
if (l != null) {
return TimeValue.timeValueMillis(l);
}
}
return null;
}
}
}

View File

@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.marvel.agent.settings;
import org.elasticsearch.common.inject.AbstractModule;
public class MarvelSettingsModule extends AbstractModule {
@Override
protected void configure() {
bind(MarvelSettingsService.class).asEagerSingleton();
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.marvel.agent.settings;
import com.google.common.collect.ImmutableList;
import org.elasticsearch.cluster.settings.ClusterDynamicSettings;
import org.elasticsearch.cluster.settings.DynamicSettings;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.marvel.MarvelPlugin;
import org.elasticsearch.marvel.agent.settings.MarvelSetting.StringArraySetting;
import org.elasticsearch.marvel.agent.settings.MarvelSetting.TimeValueSetting;
import org.elasticsearch.node.settings.NodeSettingsService;
import java.util.List;
public class MarvelSettingsService extends AbstractComponent implements NodeSettingsService.Listener {
private static final String PREFIX = MarvelPlugin.NAME + ".agent.";
private final List<MarvelSetting> settings;
final TimeValueSetting indexStatsTimeout = MarvelSetting.timeSetting(PREFIX + "index.stats.timeout", TimeValue.timeValueMinutes(10),
"Timeout value when collecting Index Stats (default to 10m)");
final StringArraySetting indices = MarvelSetting.arraySetting(PREFIX + "indices", Strings.EMPTY_ARRAY,
"List of indices names whose stats will be exported (default to all indices)");
MarvelSettingsService(Settings clusterSettings) {
super(clusterSettings);
// List of marvel settings
ImmutableList.Builder<MarvelSetting> builder = ImmutableList.builder();
builder.add(indexStatsTimeout);
builder.add(indices);
this.settings = builder.build();
logger.trace("initializing marvel settings:");
for (MarvelSetting setting : settings) {
// Initialize all settings and register them as a dynamic settings
if (setting.onInit(clusterSettings)) {
logger.trace("\t{} ({}) initialized to [{}]", setting.getName(), setting.getDescription(), setting.getValueAsString());
} else {
logger.trace("\t{} ({}) initialized", setting.getName(), setting.getDescription());
}
}
}
@Inject
public MarvelSettingsService(Settings clusterSettings, NodeSettingsService nodeSettingsService, @ClusterDynamicSettings DynamicSettings dynamicSettings) {
this(clusterSettings);
logger.trace("registering dynamic marvel settings:");
for (MarvelSetting setting : settings) {
if (setting.isDynamic()) {
logger.trace("dynamic setting [{}] registered", setting.getName());
dynamicSettings.addDynamicSetting(setting.dynamicSettingName());
}
}
logger.trace("registering the service as a node settings listener");
nodeSettingsService.addListener(this);
}
@Override
public void onRefreshSettings(Settings clusterSettings) {
for (MarvelSetting setting : settings) {
if (setting.onRefresh(clusterSettings)) {
logger.trace("setting [{}] updated to [{}]", setting.getName(), setting.getValueAsString());
}
}
}
public TimeValue indexStatsTimeout() {
return indexStatsTimeout.getValue();
}
public String[] indices() {
return indices.getValue();
}
}

View File

@ -9,6 +9,7 @@ import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.marvel.agent.exporter.MarvelDoc;
import org.elasticsearch.marvel.agent.settings.MarvelSettingsService;
import org.elasticsearch.test.ElasticsearchSingleNodeTest;
import org.junit.Test;
@ -107,6 +108,10 @@ public class IndexStatsCollectorTests extends ElasticsearchSingleNodeTest {
}
private IndexStatsCollector newIndexStatsCollector() {
return new IndexStatsCollector(getInstanceFromNode(Settings.class), getInstanceFromNode(ClusterService.class), getInstanceFromNode(ClusterName.class), client());
return new IndexStatsCollector(getInstanceFromNode(Settings.class),
getInstanceFromNode(ClusterService.class),
getInstanceFromNode(ClusterName.class),
client(),
getInstanceFromNode(MarvelSettingsService.class));
}
}

View File

@ -0,0 +1,151 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.marvel.agent.settings;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
import static org.hamcrest.Matchers.equalTo;
public class MarvelSettingTests extends ElasticsearchTestCase {
@Test
public void testBooleanMarvelSetting() {
String name = randomAsciiOfLength(10);
String description = randomAsciiOfLength(20);
Boolean defaultValue = null;
if (randomBoolean()) {
defaultValue = randomBoolean();
}
MarvelSetting.BooleanSetting setting = MarvelSetting.booleanSetting(name, defaultValue, description);
assertThat(setting.getName(), equalTo(name));
assertThat(setting.getDescription(), equalTo(description));
assertThat(setting.getDefaultValue(), equalTo(defaultValue));
setting.onInit(settingsBuilder().build());
assertThat(setting.getValue(), equalTo(defaultValue));
setting.onInit(settingsBuilder().put(name, Boolean.FALSE).build());
assertFalse(setting.getValue());
setting.onRefresh(settingsBuilder().put(name, Boolean.TRUE).build());
assertTrue(setting.getValue());
}
@Test
public void testTimeValueMarvelSetting() {
String name = randomAsciiOfLength(10);
String description = randomAsciiOfLength(20);
TimeValue defaultValue = null;
if (randomBoolean()) {
defaultValue = randomTimeValue();
}
MarvelSetting.TimeValueSetting setting = MarvelSetting.timeSetting(name, defaultValue, description);
assertThat(setting.getName(), equalTo(name));
assertThat(setting.getDescription(), equalTo(description));
if (defaultValue == null) {
assertNull(setting.getDefaultValue());
} else {
assertThat(setting.getDefaultValue().millis(), equalTo(defaultValue.millis()));
}
setting.onInit(settingsBuilder().build());
if (defaultValue == null) {
assertNull(setting.getValue());
} else {
assertThat(setting.getValue().millis(), equalTo(defaultValue.millis()));
}
setting.onInit(settingsBuilder().put(name, 15000L).build());
assertThat(setting.getValue().millis(), equalTo(15000L));
TimeValue updated = randomTimeValue();
setting.onInit(settingsBuilder().put(name, updated.toString()).build());
assertThat(setting.getValue().millis(), equalTo(updated.millis()));
updated = randomTimeValue();
setting.onRefresh(settingsBuilder().put(name, updated.toString()).build());
assertThat(setting.getValue().millis(), equalTo(updated.millis()));
}
@Test
public void testStringMarvelSetting() {
String name = randomAsciiOfLength(10);
String description = randomAsciiOfLength(20);
String defaultValue = null;
if (randomBoolean()) {
defaultValue = randomAsciiOfLength(15);
}
MarvelSetting.StringSetting setting = MarvelSetting.stringSetting(name, defaultValue, description);
assertThat(setting.getName(), equalTo(name));
assertThat(setting.getDescription(), equalTo(description));
assertThat(setting.getDefaultValue(), equalTo(defaultValue));
setting.onInit(settingsBuilder().build());
assertThat(setting.getValue(), equalTo(defaultValue));
String updated = randomAsciiOfLength(15);
setting.onInit(settingsBuilder().put(name, updated).build());
assertThat(setting.getValue(), equalTo(updated));
updated = randomAsciiOfLength(15);
setting.onRefresh(settingsBuilder().put(name, updated).build());
assertThat(setting.getValue(), equalTo(updated));
}
@Test
public void testStringArrayMarvelSetting() {
String name = randomAsciiOfLength(10);
String description = randomAsciiOfLength(20);
String[] defaultValue = null;
if (randomBoolean()) {
defaultValue = randomStringArray();
}
MarvelSetting.StringArraySetting setting = MarvelSetting.arraySetting(name, defaultValue, description);
assertThat(setting.getName(), equalTo(name));
assertThat(setting.getDescription(), equalTo(description));
if (defaultValue == null) {
assertNull(setting.getDefaultValue());
} else {
assertArrayEquals(setting.getDefaultValue(), defaultValue);
}
setting.onInit(settingsBuilder().build());
if (defaultValue == null) {
assertArrayEquals(setting.getValue(), Strings.EMPTY_ARRAY);
} else {
assertArrayEquals(setting.getValue(), defaultValue);
}
String[] updated = randomStringArray();
setting.onInit(settingsBuilder().put(name, Strings.arrayToCommaDelimitedString(updated)).build());
assertArrayEquals(setting.getValue(), updated);
updated = randomStringArray();
setting.onRefresh(settingsBuilder().put(name, Strings.arrayToCommaDelimitedString(updated)).build());
assertArrayEquals(setting.getValue(), updated);
}
private TimeValue randomTimeValue() {
return TimeValue.parseTimeValue(randomFrom("10ms", "1.5s", "1.5m", "1.5h", "1.5d", "1000d"), null, getClass().getSimpleName() + ".unit");
}
private String[] randomStringArray() {
int n = randomIntBetween(1, 5);
String[] values = new String[n];
for (int i = 0; i < n; i++) {
values[i] = randomAsciiOfLength(5);
}
return values;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.marvel.agent.settings;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
import static org.hamcrest.Matchers.equalTo;
public class MarvelSettingsServiceTests extends ElasticsearchTestCase {
@Test
public void testMarvelSettingService() {
MarvelSettingsService service = new MarvelSettingsService(Settings.EMPTY);
TimeValue indexStatsTimeout = service.indexStatsTimeout();
assertNotNull(indexStatsTimeout);
String[] indices = service.indices();
assertNotNull(indices);
TimeValue updatedIndexStatsTimeout = TimeValue.timeValueSeconds(60L);
String[] updatedIndices = new String[]{"index-0", "index-1"};
Settings settings = settingsBuilder()
.put(service.indexStatsTimeout.getName(), updatedIndexStatsTimeout)
.put(service.indices.getName(), Strings.arrayToCommaDelimitedString(updatedIndices))
.build();
service.onRefreshSettings(settings);
assertThat(service.indexStatsTimeout(), equalTo(updatedIndexStatsTimeout));
assertArrayEquals(service.indices(), updatedIndices);
}
}