org.glowroot.agent.config.ConfigService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of glowroot-agent-it-harness Show documentation
Show all versions of glowroot-agent-it-harness Show documentation
Glowroot Agent Integration Test Harness
/*
* Copyright 2011-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.glowroot.agent.config;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.glowroot.agent.shaded.com.fasterxml.jackson.core.type.TypeReference;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.base.Splitter;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.ImmutableList;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Lists;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Maps;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Sets;
import org.glowroot.agent.shaded.org.checkerframework.checker.nullness.qual.Nullable;
import org.immutables.value.Value;
import org.glowroot.agent.shaded.org.glowroot.agent.shaded.org.slf4j.Logger;
import org.glowroot.agent.shaded.org.glowroot.agent.shaded.org.slf4j.LoggerFactory;
import org.glowroot.agent.plugin.api.config.ConfigListener;
import org.glowroot.agent.util.JavaVersion;
import org.glowroot.agent.shaded.org.glowroot.common.config.AdvancedConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.AlertConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.GaugeConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.ImmutableAdvancedConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.ImmutableAlertConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.ImmutableGaugeConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.ImmutableInstrumentationConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.ImmutableJvmConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.ImmutableMBeanAttribute;
import org.glowroot.agent.shaded.org.glowroot.common.config.ImmutableSyntheticMonitorConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.ImmutableTransactionConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.ImmutableUiDefaultsConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.ImmutableUserRecordingConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.InstrumentationConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.JvmConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.PropertyValue;
import org.glowroot.agent.shaded.org.glowroot.common.config.PropertyValue.PropertyType;
import org.glowroot.agent.shaded.org.glowroot.common.config.SyntheticMonitorConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.TransactionConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.UiDefaultsConfig;
import org.glowroot.agent.shaded.org.glowroot.common.config.UserRecordingConfig;
import org.glowroot.agent.shaded.org.glowroot.common.util.OnlyUsedByTests;
import org.glowroot.agent.shaded.org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig;
public class ConfigService {
private static final Logger logger = LoggerFactory.getLogger(ConfigService.class);
private static final long GAUGE_COLLECTION_INTERVAL_MILLIS =
Long.getLong("glowroot.internal.gaugeCollectionIntervalMillis", 5000);
private final ConfigFile configFile;
private final ImmutableList pluginDescriptors;
private final Set configListeners = Sets.newCopyOnWriteArraySet();
private final Set pluginConfigListeners = Sets.newCopyOnWriteArraySet();
private volatile TransactionConfig transactionConfig;
private volatile JvmConfig jvmConfig;
private volatile UiDefaultsConfig uiDefaultsConfig;
private volatile UserRecordingConfig userRecordingConfig;
private volatile AdvancedConfig advancedConfig;
private volatile ImmutableList gaugeConfigs;
private volatile ImmutableList syntheticMonitorConfigs;
private volatile ImmutableList alertConfigs;
private volatile ImmutableList pluginConfigs;
private volatile ImmutableList instrumentationConfigs;
// memory barrier is used to ensure memory visibility of config values
private volatile boolean memoryBarrier;
public static ConfigService create(List confDirs, boolean configReadOnly,
List pluginDescriptors) {
ConfigService configService =
new ConfigService(confDirs, configReadOnly, pluginDescriptors);
// it's nice to update config.json on startup if it is missing some/all config properties so
// that the file contents can be reviewed/updated/copied if desired
try {
configService.writeAll();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return configService;
}
private ConfigService(List confDirs, boolean configReadOnly,
List pluginDescriptors) {
configFile = new ConfigFile(confDirs, configReadOnly);
this.pluginDescriptors = ImmutableList.copyOf(pluginDescriptors);
TransactionConfig transactionConfig =
configFile.getConfig("transactions", ImmutableTransactionConfig.class);
if (transactionConfig == null) {
this.transactionConfig = ImmutableTransactionConfig.builder().build();
} else {
this.transactionConfig = transactionConfig;
}
JvmConfig jvmConfig = configFile.getConfig("jvm", ImmutableJvmConfig.class);
if (jvmConfig == null) {
this.jvmConfig = ImmutableJvmConfig.builder().build();
} else {
this.jvmConfig = jvmConfig;
}
UiDefaultsConfig uiDefaultsConfig =
configFile.getConfig("uiDefaults", ImmutableUiDefaultsConfig.class);
if (uiDefaultsConfig == null) {
this.uiDefaultsConfig = ImmutableUiDefaultsConfig.builder().build();
} else {
this.uiDefaultsConfig = uiDefaultsConfig;
}
UserRecordingConfig userRecordingConfig =
configFile.getConfig("userRecording", ImmutableUserRecordingConfig.class);
if (userRecordingConfig == null) {
this.userRecordingConfig = ImmutableUserRecordingConfig.builder().build();
} else {
this.userRecordingConfig = userRecordingConfig;
}
AdvancedConfig advancedConfig =
configFile.getConfig("advanced", ImmutableAdvancedConfig.class);
if (advancedConfig == null) {
this.advancedConfig = ImmutableAdvancedConfig.builder().build();
} else {
this.advancedConfig = advancedConfig;
}
List gaugeConfigs =
configFile.getConfig("gauges", new TypeReference>() {});
if (gaugeConfigs == null) {
this.gaugeConfigs = getDefaultGaugeConfigs();
} else {
this.gaugeConfigs = ImmutableList.copyOf(gaugeConfigs);
}
List syntheticMonitorConfigs = configFile.getConfig(
"syntheticMonitors", new TypeReference>() {});
if (syntheticMonitorConfigs == null) {
this.syntheticMonitorConfigs = ImmutableList.of();
} else {
this.syntheticMonitorConfigs =
ImmutableList.copyOf(syntheticMonitorConfigs);
}
List alertConfigs =
configFile.getConfig("alerts", new TypeReference>() {});
if (alertConfigs == null) {
this.alertConfigs = ImmutableList.of();
} else {
this.alertConfigs = ImmutableList.copyOf(alertConfigs);
}
List pluginConfigs = configFile.getConfig("plugins",
new TypeReference>() {});
this.pluginConfigs = fixPluginConfigs(pluginConfigs, pluginDescriptors);
List instrumentationConfigs = configFile.getConfig(
"instrumentation", new TypeReference>() {});
if (instrumentationConfigs == null) {
this.instrumentationConfigs = ImmutableList.of();
} else {
this.instrumentationConfigs =
ImmutableList.copyOf(instrumentationConfigs);
}
for (InstrumentationConfig instrumentationConfig : this.instrumentationConfigs) {
instrumentationConfig.logValidationErrorsIfAny();
}
}
public TransactionConfig getTransactionConfig() {
return transactionConfig;
}
public JvmConfig getJvmConfig() {
return jvmConfig;
}
public UiDefaultsConfig getUiDefaultsConfig() {
return uiDefaultsConfig;
}
public UserRecordingConfig getUserRecordingConfig() {
return userRecordingConfig;
}
public AdvancedConfig getAdvancedConfig() {
return advancedConfig;
}
public List getGaugeConfigs() {
return gaugeConfigs;
}
public ImmutableList getAlertConfigs() {
return alertConfigs;
}
public ImmutableList getPluginConfigs() {
return pluginConfigs;
}
public @Nullable PluginConfig getPluginConfig(String pluginId) {
for (PluginConfig pluginConfig : pluginConfigs) {
if (pluginId.equals(pluginConfig.id())) {
return pluginConfig;
}
}
return null;
}
public List getInstrumentationConfigs() {
return instrumentationConfigs;
}
public long getGaugeCollectionIntervalMillis() {
return GAUGE_COLLECTION_INTERVAL_MILLIS;
}
public AgentConfig getAgentConfig() {
AgentConfig.Builder builder = AgentConfig.newBuilder()
.setTransactionConfig(transactionConfig.toProto());
for (GaugeConfig gaugeConfig : gaugeConfigs) {
builder.addGaugeConfig(gaugeConfig.toProto());
}
builder.setJvmConfig(jvmConfig.toProto());
for (SyntheticMonitorConfig syntheticMonitorConfig : syntheticMonitorConfigs) {
builder.addSyntheticMonitorConfig(syntheticMonitorConfig.toProto());
}
for (AlertConfig alertConfig : alertConfigs) {
builder.addAlertConfig(alertConfig.toProto());
}
builder.setUiDefaultsConfig(uiDefaultsConfig.toProto());
for (PluginConfig pluginConfig : pluginConfigs) {
builder.addPluginConfig(pluginConfig.toProto());
}
for (InstrumentationConfig instrumentationConfig : instrumentationConfigs) {
builder.addInstrumentationConfig(instrumentationConfig.toProto());
}
builder.setUserRecordingConfig(userRecordingConfig.toProto());
builder.setAdvancedConfig(advancedConfig.toProto());
return builder.build();
}
public void addConfigListener(ConfigListener listener) {
configListeners.add(listener);
listener.onChange();
}
public void addPluginConfigListener(ConfigListener listener) {
pluginConfigListeners.add(listener);
}
public void updateTransactionConfig(TransactionConfig config) throws IOException {
configFile.writeConfig("transactions", config);
transactionConfig = config;
notifyConfigListeners();
}
public void updateGaugeConfigs(List configs) throws IOException {
configFile.writeConfig("gauges", configs);
gaugeConfigs = ImmutableList.copyOf(configs);
notifyConfigListeners();
}
public void updateJvmConfig(JvmConfig config) throws IOException {
configFile.writeConfig("jvm", config);
jvmConfig = config;
notifyConfigListeners();
}
public void updateSyntheticMonitorConfigs(List configs)
throws IOException {
configFile.writeConfig("syntheticMonitors", configs);
syntheticMonitorConfigs = ImmutableList.copyOf(configs);
notifyConfigListeners();
}
public void updateAlertConfigs(List configs) throws IOException {
configFile.writeConfig("alerts", configs);
alertConfigs = ImmutableList.copyOf(configs);
notifyConfigListeners();
}
public void updateUiDefaultsConfig(UiDefaultsConfig config) throws IOException {
configFile.writeConfig("uiDefaults", config);
uiDefaultsConfig = config;
notifyConfigListeners();
}
public void updatePluginConfigs(List configs) throws IOException {
// configs passed in are already sorted
configFile.writeConfig("plugins", stripEmptyPluginConfigs(configs));
pluginConfigs = ImmutableList.copyOf(configs);
notifyAllPluginConfigListeners();
}
public void updateInstrumentationConfigs(List configs)
throws IOException {
configFile.writeConfig("instrumentation", configs);
instrumentationConfigs = ImmutableList.copyOf(configs);
notifyConfigListeners();
}
public void updateUserRecordingConfig(UserRecordingConfig config) throws IOException {
configFile.writeConfig("userRecording", config);
userRecordingConfig = config;
notifyConfigListeners();
}
public void updateAdvancedConfig(AdvancedConfig config) throws IOException {
configFile.writeConfig("advanced", config);
advancedConfig = config;
notifyConfigListeners();
}
public void updateAllConfig(AllConfig config) throws IOException {
Map configs = Maps.newHashMap();
configs.put("transactions", config.transaction());
configs.put("jvm", config.jvm());
configs.put("uiDefaults", config.uiDefaults());
configs.put("userRecording", config.userRecording());
configs.put("advanced", config.advanced());
configs.put("gauges", config.gauges());
configs.put("syntheticMonitors", config.syntheticMonitors());
configs.put("alerts", config.alerts());
configs.put("plugins", stripEmptyPluginConfigs(config.plugins()));
configs.put("instrumentation", config.instrumentation());
configFile.writeAllConfigs(configs);
this.transactionConfig = config.transaction();
this.jvmConfig = config.jvm();
this.uiDefaultsConfig = config.uiDefaults();
this.userRecordingConfig = config.userRecording();
this.advancedConfig = config.advanced();
this.gaugeConfigs = ImmutableList.copyOf(config.gauges());
this.syntheticMonitorConfigs = ImmutableList.copyOf(config.syntheticMonitors());
this.alertConfigs = ImmutableList.copyOf(config.alerts());
this.pluginConfigs = ImmutableList.copyOf(config.plugins());
this.instrumentationConfigs = ImmutableList.copyOf(config.instrumentation());
notifyConfigListeners();
notifyAllPluginConfigListeners();
}
public boolean readMemoryBarrier() {
return memoryBarrier;
}
public void writeMemoryBarrier() {
memoryBarrier = true;
}
// the updated config is not passed to the listeners to avoid the race condition of multiple
// config updates being sent out of order, instead listeners must call get*Config() which will
// never return the updates out of order (at worst it may return the most recent update twice
// which is ok)
private void notifyConfigListeners() {
for (ConfigListener configListener : configListeners) {
configListener.onChange();
}
}
private void notifyAllPluginConfigListeners() {
for (ConfigListener listener : pluginConfigListeners) {
listener.onChange();
}
writeMemoryBarrier();
}
@OnlyUsedByTests
public void setSlowThresholdToZero() throws IOException {
transactionConfig = ImmutableTransactionConfig.copyOf(transactionConfig)
.withSlowThresholdMillis(0);
writeAll();
notifyConfigListeners();
}
@OnlyUsedByTests
public void resetConfig() throws IOException {
transactionConfig = ImmutableTransactionConfig.builder()
.slowThresholdMillis(0)
.build();
jvmConfig = ImmutableJvmConfig.builder().build();
uiDefaultsConfig = ImmutableUiDefaultsConfig.builder().build();
userRecordingConfig = ImmutableUserRecordingConfig.builder().build();
advancedConfig = ImmutableAdvancedConfig.builder().build();
gaugeConfigs = getDefaultGaugeConfigs();
syntheticMonitorConfigs = ImmutableList.of();
alertConfigs = ImmutableList.of();
pluginConfigs =
fixPluginConfigs(ImmutableList.of(), pluginDescriptors);
instrumentationConfigs = ImmutableList.of();
writeAll();
notifyConfigListeners();
notifyAllPluginConfigListeners();
}
private void writeAll() throws IOException {
Map configs = Maps.newHashMap();
configs.put("transactions", transactionConfig);
configs.put("jvm", jvmConfig);
configs.put("uiDefaults", uiDefaultsConfig);
configs.put("userRecording", userRecordingConfig);
configs.put("advanced", advancedConfig);
configs.put("gauges", gaugeConfigs);
configs.put("syntheticMonitors", syntheticMonitorConfigs);
configs.put("alerts", alertConfigs);
configs.put("plugins", stripEmptyPluginConfigs(pluginConfigs));
configs.put("instrumentation", instrumentationConfigs);
configFile.writeAllConfigsOnStartup(configs);
}
private static List stripEmptyPluginConfigs(List configs) {
List nonEmptyConfigs = Lists.newArrayList();
for (PluginConfig config : configs) {
if (!config.properties().isEmpty()) {
nonEmptyConfigs.add(config);
}
}
return nonEmptyConfigs;
}
private static ImmutableList getDefaultGaugeConfigs() {
List defaultGaugeConfigs = Lists.newArrayList();
defaultGaugeConfigs.add(ImmutableGaugeConfig.builder()
.mbeanObjectName("java.lang:type=Memory")
.addMbeanAttributes(ImmutableMBeanAttribute.of("HeapMemoryUsage.used", false))
.build());
defaultGaugeConfigs.add(ImmutableGaugeConfig.builder()
.mbeanObjectName("java.lang:type=GarbageCollector,name=*")
.addMbeanAttributes(ImmutableMBeanAttribute.of("CollectionCount", true))
.addMbeanAttributes(ImmutableMBeanAttribute.of("CollectionTime", true))
.build());
defaultGaugeConfigs.add(ImmutableGaugeConfig.builder()
.mbeanObjectName("java.lang:type=MemoryPool,name=*")
.addMbeanAttributes(ImmutableMBeanAttribute.of("Usage.used", false))
.build());
ImmutableGaugeConfig.Builder operatingSystemMBean = ImmutableGaugeConfig.builder()
.mbeanObjectName("java.lang:type=OperatingSystem")
.addMbeanAttributes(ImmutableMBeanAttribute.of("FreePhysicalMemorySize", false));
if (!JavaVersion.isJava6()) {
// these are only available since 1.7
operatingSystemMBean
.addMbeanAttributes(ImmutableMBeanAttribute.of("ProcessCpuLoad", false));
operatingSystemMBean
.addMbeanAttributes(ImmutableMBeanAttribute.of("SystemCpuLoad", false));
}
defaultGaugeConfigs.add(operatingSystemMBean.build());
return ImmutableList.copyOf(defaultGaugeConfigs);
}
public static ImmutableList fixPluginConfigs(
@Nullable List filePluginConfigs,
List pluginDescriptors) {
Map filePluginConfigMap = Maps.newHashMap();
if (filePluginConfigs != null) {
for (ImmutablePluginConfigTemp pluginConfig : filePluginConfigs) {
filePluginConfigMap.put(pluginConfig.id(), pluginConfig);
}
}
List accuratePluginConfigs = Lists.newArrayList();
for (PluginDescriptor pluginDescriptor : pluginDescriptors) {
PluginConfigTemp filePluginConfig = filePluginConfigMap.get(pluginDescriptor.id());
ImmutablePluginConfig.Builder builder = ImmutablePluginConfig.builder()
.pluginDescriptor(pluginDescriptor);
for (PropertyDescriptor propertyDescriptor : pluginDescriptor.properties()) {
builder.putProperties(propertyDescriptor.name(),
getPropertyValue(filePluginConfig, propertyDescriptor));
}
accuratePluginConfigs.add(builder.build());
}
return ImmutableList.copyOf(accuratePluginConfigs);
}
private static PropertyValue getPropertyValue(@Nullable PluginConfigTemp pluginConfig,
PropertyDescriptor propertyDescriptor) {
if (pluginConfig == null) {
return propertyDescriptor.getValidatedNonNullDefaultValue();
}
PropertyValue propertyValue = getValidatedPropertyValue(pluginConfig.properties(),
propertyDescriptor.name(), propertyDescriptor.type());
if (propertyValue == null) {
return propertyDescriptor.getValidatedNonNullDefaultValue();
}
return propertyValue;
}
private static @Nullable PropertyValue getValidatedPropertyValue(
Map properties, String propertyName, PropertyType propertyType) {
PropertyValue propertyValue = properties.get(propertyName);
if (propertyValue == null) {
return null;
}
Object value = propertyValue.value();
if (value == null) {
return PropertyDescriptor.getDefaultValue(propertyType);
}
if (PropertyDescriptor.isValidType(value, propertyType)) {
return propertyValue;
} else if (value instanceof String && propertyType == PropertyType.LIST) {
// handle upgrading from comma-separated string properties to list properties
return new PropertyValue(
Splitter.on(',').trimResults().omitEmptyStrings().splitToList((String) value));
} else {
logger.warn("invalid value for plugin property: {}", propertyName);
return PropertyDescriptor.getDefaultValue(propertyType);
}
}
@Value.Immutable
interface PluginConfigTemp {
String id();
Map properties();
}
}