Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ public String name() {

@Override
public void init(EarlyInitAgentConfig earlyConfig) {
int limit =
earlyConfig.getInt("otel.javaagent.logging.application.logs-buffer-max-records", 2048);
int limit = earlyConfig.getLoggingApplicationLogsBufferMaxRecords();
InMemoryLogStore inMemoryLogStore = new InMemoryLogStore(limit);
ApplicationLoggerFactory loggerFactory = new ApplicationLoggerFactory(inMemoryLogStore);
// register a shutdown hook that'll dump the logs to stderr in case something goes wrong
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void init(EarlyInitAgentConfig earlyConfig) {
setSystemPropertyDefault(
SIMPLE_LOGGER_DATE_TIME_FORMAT_PROPERTY, SIMPLE_LOGGER_DATE_TIME_FORMAT_DEFAULT);

if (earlyConfig.getBoolean("otel.javaagent.debug", false)) {
if (earlyConfig.getDebug()) {
setSystemPropertyDefault(SIMPLE_LOGGER_DEFAULT_LOG_LEVEL_PROPERTY, "DEBUG");
setSystemPropertyDefault(SIMPLE_LOGGER_PREFIX + "okhttp3.internal.http2", "INFO");
setSystemPropertyDefault(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public static void installBytebuddyAgent(
}

logVersionInfo();
if (earlyConfig.getBoolean(JAVAAGENT_ENABLED_CONFIG, true)) {
if (earlyConfig.getEnabled()) {
List<AgentListener> agentListeners = loadOrdered(AgentListener.class, extensionClassLoader);
installBytebuddyAgent(inst, extensionClassLoader, agentListeners, earlyConfig);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public void start() {
// allows loading instrumenter customizers from agent and extensions
ServiceLoaderUtil.setLoadFunction(clazz -> ServiceLoader.load(clazz, extensionClassLoader));

String loggerImplementationName = earlyConfig.getString("otel.javaagent.logging");
String loggerImplementationName = earlyConfig.getLogging();
// default to the built-in stderr slf4j-simple logger
if (loggerImplementationName == null) {
loggerImplementationName = "simple";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public static ClassLoader getInstance(

includeEmbeddedExtensionsIfFound(extensions, javaagentFile);

extensions.addAll(parseLocation(earlyConfig.getString(EXTENSIONS_CONFIG), javaagentFile));
extensions.addAll(parseLocation(earlyConfig.getExtensions(), javaagentFile));

// TODO when logging is configured add warning about deprecated property

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ static ConfigProperties getDeclarativeConfigBridgedProperties(
// these properties are used to initialize the SDK before the configuration file
// is loaded for consistency, we pass them to the bridge, so that they can be read
// later with the same value from the {@link DeclarativeConfigPropertiesBridge}
.addOverride("otel.javaagent.debug", earlyConfig.getBoolean("otel.javaagent.debug", false))
.addOverride("otel.javaagent.logging", earlyConfig.getString("otel.javaagent.logging"))
.addOverride("otel.javaagent.debug", earlyConfig.getDebug())
.addOverride("otel.javaagent.logging", earlyConfig.getLogging())
.buildFromInstrumentationConfig(configProvider.getInstrumentationConfig());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.tooling.config;

import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty;
import static java.util.logging.Level.WARNING;

import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil;
import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.logging.Logger;
import javax.annotation.Nullable;

final class DeclarativeConfigurationFile {

static final String DECLARATIVE_CONFIG_FILE_PROPERTY = "otel.experimental.config.file";

private static DeclarativeConfigProperties configProperties;

// this class is used early, and must not use logging in most of its methods
// in case any file loading/parsing error occurs, we save the error message and log it later, when
// the logging subsystem is initialized
@Nullable private static String fileLoadErrorMessage;

static boolean isConfigured() {
String configFilePath = ConfigPropertiesUtil.getString(DECLARATIVE_CONFIG_FILE_PROPERTY);
return configFilePath != null && !configFilePath.isEmpty();
}

static DeclarativeConfigProperties getProperties() {
if (configProperties == null) {
configProperties = loadConfigFile();
}
return configProperties;
}

// visible for tests
static void resetForTest() {
configProperties = null;
fileLoadErrorMessage = null;
}

// visible for tests
static DeclarativeConfigProperties loadConfigFile() {
String configFilePath = ConfigPropertiesUtil.getString(DECLARATIVE_CONFIG_FILE_PROPERTY);
if (configFilePath == null || configFilePath.isEmpty()) {
return empty();
}

// Normalizing tilde (~) paths for unix systems
configFilePath = configFilePath.replaceFirst("^~", System.getProperty("user.home"));

Path file = Paths.get(configFilePath);
if (!Files.exists(file)) {
fileLoadErrorMessage = "Declarative configuration file \"" + configFilePath + "\" not found.";
return empty();
}

try (InputStream is = Files.newInputStream(file)) {
return DeclarativeConfiguration.toConfigProperties(is);
} catch (IOException e) {
fileLoadErrorMessage =
"Declarative configuration file \""
+ configFilePath
+ "\" cannot be accessed or correctly parsed.";
return empty();
}
}

static void logErrorIfAny() {
if (fileLoadErrorMessage != null) {
Logger.getLogger(DeclarativeConfigurationFile.class.getName())
.log(WARNING, fileLoadErrorMessage);
}
}

private DeclarativeConfigurationFile() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

package io.opentelemetry.javaagent.tooling.config;

import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty;

import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil;
import java.util.Map;
import javax.annotation.Nullable;
Expand All @@ -15,33 +18,98 @@
*/
public final class EarlyInitAgentConfig {

@Nullable private final String logging;
@Nullable private final String extensions;
private final boolean enabled;
private final boolean debug;
private final int loggingApplicationLogsBufferMaxRecords;
private final boolean fieldInjectionEnabled;

public static EarlyInitAgentConfig create() {
if (DeclarativeConfigurationFile.isConfigured()) {
return new EarlyInitAgentConfig(DeclarativeConfigurationFile.getProperties());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would involve Jackson too early and cause class loading issues - I think polluting the wrong class loader.
@laurit explained that to me when I tried to do that last year.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link doesn't work:

  private static OpenTelemetryConfigurationModel loadConfigurationModel(String configurationFile) {
    try (FileInputStream fis = new FileInputStream(configurationFile)) {
      return DeclarativeConfiguration.parse(fis);
    } catch (IOException e) {
      ErrorBuffer.addErrorMessage(
          "Error reading configuration file: " + configurationFile + ". " + e.getMessage());
      return null;
    }
  }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yes, I'll have to think on this some more

}
return new EarlyInitAgentConfig(ConfigurationFile.getProperties());
}

private final Map<String, String> configFileContents;
private EarlyInitAgentConfig(@Nullable DeclarativeConfigProperties properties) {
DeclarativeConfigProperties agent =
(properties != null ? properties : empty())
.getStructured("instrumentation/development", empty())
.getStructured("java", empty())
.getStructured("agent", empty());

this.logging = agent.getString("logging");
this.extensions = agent.getString("extensions");
this.enabled = agent.getBoolean("enabled", true);
this.debug = agent.getBoolean("debug", false);
this.loggingApplicationLogsBufferMaxRecords =
agent
.getStructured("logging", empty())
.getStructured("application", empty())
.getInt("logs_buffer_max_records", 2048);
this.fieldInjectionEnabled =
agent.getStructured("field_injection/development", empty()).getBoolean("enabled", true);
}

private EarlyInitAgentConfig(Map<String, String> configFileContents) {
this.configFileContents = configFileContents;
this.logging = loadString(configFileContents, "otel.javaagent.logging");
this.extensions = loadString(configFileContents, "otel.javaagent.extensions");
this.enabled = loadBoolean(configFileContents, "otel.javaagent.enabled", true);
this.debug = loadBoolean(configFileContents, "otel.javaagent.debug", false);
this.loggingApplicationLogsBufferMaxRecords =
loadInt(
configFileContents, "otel.javaagent.logging.application.logs-buffer-max-records", 2048);
this.fieldInjectionEnabled =
loadBoolean(
configFileContents, "otel.javaagent.experimental.field-injection.enabled", true);
}

@Nullable
public String getLogging() {
return logging;
}

@Nullable
public String getExtensions() {
return extensions;
}

public boolean getEnabled() {
return enabled;
}

public boolean getDebug() {
return debug;
}

public int getLoggingApplicationLogsBufferMaxRecords() {
return loggingApplicationLogsBufferMaxRecords;
}

public boolean getFieldInjectionEnabled() {
return fieldInjectionEnabled;
}

@Nullable
public String getString(String propertyName) {
private static String loadString(Map<String, String> configFileContents, String propertyName) {
String value = ConfigPropertiesUtil.getString(propertyName);
if (value != null) {
return value;
}
return configFileContents.get(propertyName);
}

public boolean getBoolean(String propertyName, boolean defaultValue) {
private static boolean loadBoolean(
Map<String, String> configFileContents, String propertyName, boolean defaultValue) {
String configFileValueStr = configFileContents.get(propertyName);
boolean configFileValue =
configFileValueStr == null ? defaultValue : Boolean.parseBoolean(configFileValueStr);
return ConfigPropertiesUtil.getBoolean(propertyName, configFileValue);
}

public int getInt(String propertyName, int defaultValue) {
private static int loadInt(
Map<String, String> configFileContents, String propertyName, int defaultValue) {
try {
String configFileValueStr = configFileContents.get(propertyName);
int configFileValue =
Expand All @@ -54,5 +122,6 @@ public int getInt(String propertyName, int defaultValue) {

public void logEarlyConfigErrorsIfAny() {
ConfigurationFile.logErrorIfAny();
DeclarativeConfigurationFile.logErrorIfAny();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ public final class FieldBackedImplementationConfiguration {
static boolean fieldInjectionEnabled = true;

public static void configure(EarlyInitAgentConfig config) {
fieldInjectionEnabled =
config.getBoolean("otel.javaagent.experimental.field-injection.enabled", true);
fieldInjectionEnabled = config.getFieldInjectionEnabled();
}

private FieldBackedImplementationConfiguration() {}
Expand Down
Loading