From 2a2f5014933e630a4a8ca1f5de4eb401e6fed0f9 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 16 Nov 2021 16:51:21 +0100 Subject: [PATCH 01/86] Initial commit of the ActionMetricsRecorder that records metrics of IHookAction (issue #1231) --- .../inspectit/ocelot/ConfigurationServer.java | 2 +- .../ActionMetricsRecorderTestAgent.java | 55 +++++ .../config/model/metrics/MetricsSettings.java | 4 +- .../definition/MetricDefinitionSettings.java | 9 +- .../SelfMonitoringSettings.java | 9 + .../ocelot/config/default/self-monitoring.yml | 28 +++ .../core/instrumentation/hook/MethodHook.java | 29 ++- .../hook/MethodHookGenerator.java | 36 ++- .../hook/actions/ConditionalHookAction.java | 4 +- .../selfmonitoring/ActionScopeFactory.java | 30 +++ .../core/selfmonitoring/ActionScopeImpl.java | 74 ++++++ .../ActionsMetricsRecorder.java | 217 ++++++++++++++++++ .../core/selfmonitoring/IActionScope.java | 24 ++ .../selfmonitoring/LogMetricsRecorder.java | 2 +- .../core/selfmonitoring/NoopActionScope.java | 28 +++ .../ActionMetricsRecorderConfigTest.java | 54 +++++ .../ActionMetricsRecorderTest.java | 120 ++++++++++ .../docs/metrics/self-monitoring.md | 2 + 18 files changed, 695 insertions(+), 32 deletions(-) create mode 100644 inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderTestAgent.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactory.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImpl.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/IActionScope.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/NoopActionScope.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderTest.java diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/ConfigurationServer.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/ConfigurationServer.java index 9de145c17b..8bd864ab04 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/ConfigurationServer.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/ConfigurationServer.java @@ -23,7 +23,7 @@ public class ConfigurationServer { /** * Initializer which ensures that the working directory exists prior to initializing the spring context. - * This is required because otherwise the SQLite DB can't create it's database file. + * This is required because otherwise the SQLite DB can't create its database file. */ private static final ApplicationContextInitializer WORKING_DIRECTORY_INITIALIZER = (ctx) -> { InspectitServerSettings settings = Binder diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderTestAgent.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderTestAgent.java new file mode 100644 index 0000000000..65c96b748b --- /dev/null +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderTestAgent.java @@ -0,0 +1,55 @@ +package rocks.inspectit.ocelot.metrics.selfmonitoring; + +import io.opencensus.stats.*; +import io.opencensus.tags.TagValue; +import org.junit.jupiter.api.Test; +import rocks.inspectit.ocelot.metrics.MetricsSysTestBase; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +/** + * Tests the {@link rocks.inspectit.ocelot.core.selfmonitoring.ActionsMetricsRecorder} + */ +public class ActionMetricsRecorderTestAgent extends MetricsSysTestBase { + + private static final ViewManager viewManager = Stats.getViewManager(); + + @Test + public void testActionsMetricsRecorder() { + await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { + + // TODO fire some fake action + + ViewData actionExecutionTimes = viewManager.getView(View.Name.create("inspectit/self/actions/execution-time")); + ViewData actionExecutionCounts = viewManager.getView(View.Name.create("inspectit/self/actions/count")); + + // fake record some measurements + + assertThat(actionExecutionTimes).isNotNull(); + assertThat(actionExecutionTimes.getAggregationMap()).isNotNull().isNotEmpty(); + + Map.Entry, AggregationData> executionTime = actionExecutionTimes.getAggregationMap() + .entrySet() + .stream() + .findFirst() + .get(); + + // ensure that tags are present + assertThat(executionTime.getKey()).isNotEmpty(); + + System.out.println("print keys"); + System.out.println(executionTime.getKey().toArray()); + + // ensure sanity of values + long executionTimeVal = ((AggregationData.LastValueDataLong) executionTime.getValue()).getLastValue(); + assertThat(executionTimeVal).isGreaterThan(0); + + }); + } + +} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/MetricsSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/MetricsSettings.java index b5ae5577a3..c0b086c555 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/MetricsSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/MetricsSettings.java @@ -78,12 +78,14 @@ public class MetricsSettings { private StandardPollingMetricsRecorderSettings memory; /** - * Settings for {@link rocks.inspectit.ocelot.core.metrics.system.JxmMetricsRecorder} + * Settings for {@link rocks.inspectit.ocelot.core.metrics.system.JmxMetricsRecorder} */ @Valid @NotNull private JmxMetricsRecorderSettings jmx; + + @AdditionalValidation public void noDuplicateViewNames(ViolationBuilder vios) { Map viewsToMeasuresMap = new HashMap<>(); diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/definition/MetricDefinitionSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/definition/MetricDefinitionSettings.java index 401e1b31be..cea15ee6b2 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/definition/MetricDefinitionSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/metrics/definition/MetricDefinitionSettings.java @@ -46,7 +46,7 @@ public enum MeasureType { private String description; /** - * Maps view names to their defintions for the measure defined by this {@link MetricDefinitionSettings}. + * Maps view names to their definitions for the measure defined by this {@link MetricDefinitionSettings}. * If this is null, a default view is created which simply exposes the last value of the metric. */ @Singular @@ -74,12 +74,9 @@ public MetricDefinitionSettings getCopyWithDefaultsPopulated(String metricName) */ public MetricDefinitionSettings getCopyWithDefaultsPopulated(String metricName, Duration defaultTimeWindow) { val resultDescription = description == null ? metricName : description; - val result = toBuilder() - .description(resultDescription) - .clearViews(); + val result = toBuilder().description(resultDescription).clearViews(); if (!CollectionUtils.isEmpty(views)) { - views.forEach((name, def) -> - result.view(name, def.getCopyWithDefaultsPopulated(resultDescription, unit, defaultTimeWindow))); + views.forEach((name, def) -> result.view(name, def.getCopyWithDefaultsPopulated(resultDescription, unit, defaultTimeWindow))); } else { result.view(metricName, ViewDefinitionSettings.builder() .aggregation(ViewDefinitionSettings.Aggregation.LAST_VALUE) diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/SelfMonitoringSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/SelfMonitoringSettings.java index 92bdae0882..6fcb81e3a3 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/SelfMonitoringSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/SelfMonitoringSettings.java @@ -2,6 +2,9 @@ import lombok.Data; import lombok.NoArgsConstructor; +import rocks.inspectit.ocelot.config.model.metrics.StandardMetricsSettings; + +import javax.validation.Valid; @Data @NoArgsConstructor @@ -12,4 +15,10 @@ public class SelfMonitoringSettings { */ private boolean enabled; + /** + * Settings for {@link rocks.inspectit.ocelot.core.selfmonitoring.ActionsMetricsRecorder} + */ + @Valid + private StandardMetricsSettings actions; + } diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml index 0caa036220..5ee8154587 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml @@ -4,6 +4,14 @@ inspectit: self-monitoring: enabled: true + # settings regarding the capturing of action related metrics + actions: + enabled: + # if true, the execution time (duration in ms) per action will be recorded + execution-time: true + # if true, the number of executions per action will be recorded + count: true + metrics: definitions: '[inspectit/self/duration]': @@ -36,3 +44,23 @@ inspectit: '[inspectit/self/logs]': aggregation: SUM tags: {"level": true} + + '[inspectit/self/actions/execution-time]': + enabled: ${inspectit.self-monitoring.actions.enabled.execution-time} + type: LONG + unit: us + description: "the execution time of the action" + views: + '[inspectit/self/actions/execution-time]': + tags: + action_name: true + '[inspectit/self/actions/count]': + enabled: ${inspectit.self-monitoring.actions.enabled.count} + type: LONG + unit: action executions + description: "number of action executions" + views: + '[inspectit/self/actions/count]': + aggregation: SUM + tags: + action_name: true diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java index e4a05be41e..e3fe1a000c 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java @@ -11,6 +11,8 @@ import rocks.inspectit.ocelot.core.instrumentation.context.ContextManager; import rocks.inspectit.ocelot.core.instrumentation.context.InspectitContextImpl; import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; +import rocks.inspectit.ocelot.core.selfmonitoring.ActionScopeFactory; +import rocks.inspectit.ocelot.core.selfmonitoring.ActionsMetricsRecorder; import java.util.ArrayList; import java.util.List; @@ -52,7 +54,6 @@ public class MethodHook implements IMethodHook { */ private final CopyOnWriteArrayList activeEntryActions; - /** * The subset of {@link #exitActions}, which are actually active. * Initially, this list contains the same elements as {@link #exitActions}. @@ -64,8 +65,18 @@ public class MethodHook implements IMethodHook { */ private final MethodReflectionInformation methodInformation; + /** + * The metrics recorder for the {@link IHookAction} + */ + private final ActionsMetricsRecorder actionsMetricsRecorder; + + /** + * The factory that creates/spawns new scopes for an {@link IHookAction} + */ + private final ActionScopeFactory actionScopeFactory; + @Builder - public MethodHook(MethodHookConfiguration sourceConfiguration, ContextManager inspectitContextManager, @Singular List entryActions, @Singular List exitActions, MethodReflectionInformation methodInformation) { + public MethodHook(MethodHookConfiguration sourceConfiguration, ContextManager inspectitContextManager, @Singular List entryActions, @Singular List exitActions, MethodReflectionInformation methodInformation, ActionsMetricsRecorder actionsMetricsRecorder, ActionScopeFactory actionScopeFactory) { this.sourceConfiguration = sourceConfiguration; this.inspectitContextManager = inspectitContextManager; this.entryActions = new ArrayList<>(entryActions); @@ -73,6 +84,8 @@ public MethodHook(MethodHookConfiguration sourceConfiguration, ContextManager in this.exitActions = new ArrayList<>(exitActions); activeExitActions = new CopyOnWriteArrayList<>(exitActions); this.methodInformation = methodInformation; + this.actionsMetricsRecorder = actionsMetricsRecorder; + this.actionScopeFactory = actionScopeFactory; } @Override @@ -81,11 +94,10 @@ public InternalInspectitContext onEnter(Object[] args, Object thiz) { val executionContext = new IHookAction.ExecutionContext(args, thiz, null, null, this, inspectitContext); for (val action : activeEntryActions) { - try { + try (val scope = actionScopeFactory.getScope(action)) { action.execute(executionContext); } catch (Throwable t) { - log.error("Entry action {} executed for method {} threw an exception and from now on is disabled!", - action.getName(), methodInformation.getMethodFQN(), t); + log.error("Entry action {} executed for method {} threw an exception and from now on is disabled!", action.getName(), methodInformation.getMethodFQN(), t); activeEntryActions.remove(action); } } @@ -98,11 +110,10 @@ public InternalInspectitContext onEnter(Object[] args, Object thiz) { public void onExit(Object[] args, Object thiz, Object returnValue, Throwable thrown, InternalInspectitContext context) { val executionContext = new IHookAction.ExecutionContext(args, thiz, returnValue, thrown, this, (InspectitContextImpl) context); for (val action : activeExitActions) { - try { + try (val scope = actionScopeFactory.getScope(action)) { action.execute(executionContext); } catch (Throwable t) { - log.error("Exit action {} executed for method {} threw an exception and from now on is disabled!", - action.getName(), methodInformation.getMethodFQN(), t); + log.error("Exit action {} executed for method {} threw an exception and from now on is disabled!", action.getName(), methodInformation.getMethodFQN(), t); activeExitActions.remove(action); } } @@ -113,7 +124,7 @@ public void onExit(Object[] args, Object thiz, Object returnValue, Throwable thr * @return An exact copy of this method hook but with all deactivated actions reactivated. */ public MethodHook getResettedCopy() { - return new MethodHook(sourceConfiguration, inspectitContextManager, entryActions, exitActions, methodInformation); + return new MethodHook(sourceConfiguration, inspectitContextManager, entryActions, exitActions, methodInformation, actionsMetricsRecorder, actionScopeFactory); } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java index 6fbb26de69..1fb00e491e 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java @@ -1,7 +1,5 @@ package rocks.inspectit.ocelot.core.instrumentation.hook; -import static java.lang.Boolean.TRUE; - import com.google.common.annotations.VisibleForTesting; import io.opencensus.trace.Sampler; import io.opencensus.trace.samplers.Samplers; @@ -25,11 +23,15 @@ import rocks.inspectit.ocelot.core.instrumentation.hook.tags.CommonTagsToAttributesManager; import rocks.inspectit.ocelot.core.metrics.MeasuresAndViewsManager; import rocks.inspectit.ocelot.core.privacy.obfuscation.ObfuscationManager; +import rocks.inspectit.ocelot.core.selfmonitoring.ActionScopeFactory; +import rocks.inspectit.ocelot.core.selfmonitoring.ActionsMetricsRecorder; import rocks.inspectit.ocelot.core.tags.CommonTagsManager; import java.util.*; import java.util.stream.Collectors; +import static java.lang.Boolean.TRUE; + /** * This class is responsible for translating {@link MethodHookConfiguration}s * into executable {@link MethodHook}s. @@ -62,10 +64,16 @@ public class MethodHookGenerator { @Autowired private StackTraceSampler stackTraceSampler; + @Autowired + private ActionsMetricsRecorder actionsMetricsRecorder; + + @Autowired + private ActionScopeFactory actionScopeFactory; + /** - * Builds a executable method hook based on the given configuration. + * Builds an executable method hook based on the given configuration. * - * @param declaringClass teh class defining the method which is being hooked + * @param declaringClass the class defining the method which is being hooked * @param method a method descriptor of the hooked method * @param config the configuration to use for building the hook * @@ -96,6 +104,9 @@ public MethodHook buildHook(Class declaringClass, MethodDescription method, M buildMetricsRecorder(config).ifPresent(builder::exitAction); builder.exitActions(buildActionCalls(config.getPostExitActions(), methodInfo)); + builder.actionsMetricsRecorder(actionsMetricsRecorder); + builder.actionScopeFactory(actionScopeFactory); + return builder.build(); } @@ -107,9 +118,9 @@ private List buildTracingEntryActions(RuleTracingSettings tracing) if (TRUE.equals(tracing.getStartSpan())) { VariableAccessor name = Optional.ofNullable(tracing.getName()) - .map(variableAccessorFactory::getVariableAccessor).orElse(null); - actionBuilder - .startSpanCondition(ConditionalHookAction.getAsPredicate(tracing.getStartSpanConditions(), variableAccessorFactory)) + .map(variableAccessorFactory::getVariableAccessor) + .orElse(null); + actionBuilder.startSpanCondition(ConditionalHookAction.getAsPredicate(tracing.getStartSpanConditions(), variableAccessorFactory)) .nameAccessor(name) .spanKind(tracing.getKind()); configureSampling(tracing, actionBuilder); @@ -118,8 +129,7 @@ private List buildTracingEntryActions(RuleTracingSettings tracing) } if (tracing.getContinueSpan() != null) { - actionBuilder - .continueSpanCondition(ConditionalHookAction.getAsPredicate(tracing.getContinueSpanConditions(), variableAccessorFactory)) + actionBuilder.continueSpanCondition(ConditionalHookAction.getAsPredicate(tracing.getContinueSpanConditions(), variableAccessorFactory)) .continueSpanDataKey(tracing.getContinueSpan()); } else { actionBuilder.continueSpanCondition(ctx -> false); @@ -219,7 +229,9 @@ MetricAccessor buildMetricAccessor(MetricRecordingSettings metricSettings) { valueAccessor = variableAccessorFactory.getVariableAccessor(value); } - Map tagAccessors = metricSettings.getDataTags().entrySet().stream() + Map tagAccessors = metricSettings.getDataTags() + .entrySet() + .stream() .collect(Collectors.toMap(Map.Entry::getKey, entry -> variableAccessorFactory.getVariableAccessor(entry.getValue()))); return new MetricAccessor(metricSettings.getMetric(), valueAccessor, metricSettings.getConstantTags(), tagAccessors); @@ -232,8 +244,8 @@ private List buildActionCalls(List calls, MethodR try { result.add(actionCallGenerator.generateAndBindGenericAction(methodInfo, call)); } catch (Exception e) { - log.error("Failed to build action {} for data {} on method {}, no value will be assigned", - call.getAction().getName(), call.getName(), methodInfo.getMethodFQN(), e); + log.error("Failed to build action {} for data {} on method {}, no value will be assigned", call.getAction() + .getName(), call.getName(), methodInfo.getMethodFQN(), e); } } return result; diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/ConditionalHookAction.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/ConditionalHookAction.java index fb9385c78d..ea247e00ac 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/ConditionalHookAction.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/ConditionalHookAction.java @@ -31,8 +31,8 @@ public String getName() { } /** - * If a action contains values for the "only-if-..." settings the provider is meant to be only executed conditionally. - * Therefore in this method we wrap the call in {@link ConditionalHookAction} which check the corresponding preconditions. + * If an action contains values for the "only-if-..." settings the provider is meant to be only executed conditionally. + * Therefore, in this method we wrap the call in {@link ConditionalHookAction} which check the corresponding preconditions. * * @param conditions the definitions of the conditions to check * @param inputAction the action to execute only conditionally diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactory.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactory.java new file mode 100644 index 0000000000..f65ec5980b --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactory.java @@ -0,0 +1,30 @@ +package rocks.inspectit.ocelot.core.selfmonitoring; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import rocks.inspectit.ocelot.core.instrumentation.hook.MethodReflectionInformation; +import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; + +/** + * A singleton factory that creates {@link IActionScope} for a specific action. + */ +@Slf4j +@Component +public class ActionScopeFactory { + + @Autowired + ActionsMetricsRecorder recorder; + + /** + * Creates and returns a new {@link IActionScope} for the given {@link IHookAction}. If the {@link ActionsMetricsRecorder} is disabled, the {@link NoopActionScope} will be returned. + * + * @param action The action + * + * @return A new {@link IActionScope} for the given {@link IHookAction}. A {@link NoopActionScope} is returned if the {@link ActionsMetricsRecorder} is disabled. + */ + public IActionScope getScope(IHookAction action) { + return recorder.isEnabled() ? new ActionScopeImpl(action, recorder) : NoopActionScope.INSTANCE; + } + +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImpl.java new file mode 100644 index 0000000000..71be07f19d --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImpl.java @@ -0,0 +1,74 @@ +package rocks.inspectit.ocelot.core.selfmonitoring; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import rocks.inspectit.ocelot.core.instrumentation.hook.MethodReflectionInformation; +import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; + +import java.util.concurrent.TimeUnit; + +/** + * Functional implementation of the {@link IActionScope} + */ +@Data +@Slf4j +public class ActionScopeImpl implements IActionScope { + + /** + * The {@link IHookAction} of this scope. + */ + private IHookAction action; + + /** + * The start time of the action/scope in nanoseconds. + */ + private long startTimeNanos; + + /** + * The recorder used for recording the metrics of this {@link #action}. + */ + private ActionsMetricsRecorder recorder; + + /** + * Creates and starts a new {@link ActionScopeImpl} for the given {@link IHookAction} + * + * @param action The {@link IHookAction} of this scope + * @param recorder The {@link ActionsMetricsRecorder} + */ + public ActionScopeImpl(IHookAction action, ActionsMetricsRecorder recorder) { + this(action, recorder, true); + } + + /** + * @param action The {@link IHookAction} of this scope + * @param recorder The {@link ActionsMetricsRecorder} + * @param autoStart Whether to automatically start the scope, i.e., setting the {@link #startTimeNanos} + */ + public ActionScopeImpl(IHookAction action, ActionsMetricsRecorder recorder, boolean autoStart) { + this.action = action; + this.recorder = recorder; + if (autoStart) { + start(); + } + } + + @Override + public void start() { + // set start time of the action/scope in nanoseconds + startTimeNanos = System.nanoTime(); + } + + @Override + public void start(long startTimeNanos) { + this.startTimeNanos = startTimeNanos; + } + + @Override + public void close() { + // record the action's metrics. + val executionTimeMicros = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - startTimeNanos); + // log.info("ActionScopeImpl.close. action={}, startTime={}, endTime={}, executionTimeMillis={}", action.getName(), startTimeNanos, System.nanoTime(), executionTimeMillis); + recorder.record(action, executionTimeMicros); + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java new file mode 100644 index 0000000000..3dde7b45c2 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java @@ -0,0 +1,217 @@ +package rocks.inspectit.ocelot.core.selfmonitoring; + +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.core.instrumentation.hook.MethodHook; +import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; + +import java.util.HashMap; +import java.util.concurrent.TimeUnit; + +/** + * Recorder for {@link MethodHook} to record and expose metrics (e.g., number of invocation, execution time) of individual {@link rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction}. + */ +@Component +@Slf4j +public class ActionsMetricsRecorder extends DynamicallyActivatableService { + + @Autowired + private SelfMonitoringService selfMonitoringService; + + /** + * The prefix of the recorded metrics. + */ + private static final String METRIC_NAME_PREFIX = "actions/"; + + /** + * The metric name for the execution time of an action. + */ + private static final String EXECUTION_TIME_METRIC_NAME = "execution-time"; + + /** + * The metric name for execution count. + */ + private static final String COUNT_METRIC_NAME = "count"; + + /** + * The key of the action's name used in custom tags. + */ + private static final String ACTION_NAME_KEY = "action_name"; + + /** + * The map containing the actions' start time in nanoseconds. + */ + private HashMap actionStartTimesNanos = new HashMap<>(); + + /** + * The map containing the actions' execution counts. + */ + private HashMap actionExecutionCounts = new HashMap<>(); + + public ActionsMetricsRecorder() { + // TODO: do metrics need to be enabled in order to take self-monitoring measurements? + super("metrics.enabled", "selfMonitoring.actions"); + } + + /** + * Records the execution time of an {@link IHookAction}. + * + * @param action The action + * @param executionTimeMicros The execution time in microseconds + * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? + */ + public void record(IHookAction action, long executionTimeMicros) { + record(action.getName(), executionTimeMicros); + } + + /** + * Records the execution time of an action. + * + * @param actionName The name of the execution + * @param executionTimeMicros The execution time in microseconds + * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? + */ + public void record(String actionName, long executionTimeMicros) { + + // do nothing if this metrics recorder is not enabled + if (!isEnabled()) { + return; + } + + // log.info("recording action '{}', executionTimeMicros = {}", actionName, executionTimeMicros); + + val actionsSettings = env.getCurrentConfig().getSelfMonitoring().getActions(); + + // create custom tags + val customTags = new HashMap() {{ + put(ACTION_NAME_KEY, actionName); + }}; + + // record the action's execution time if enabled + if (actionsSettings.getEnabled().getOrDefault(EXECUTION_TIME_METRIC_NAME, false)) { + recordMeasurement(EXECUTION_TIME_METRIC_NAME, executionTimeMicros, customTags); + } + + // record the execution count increment + // TODO: do I need to store the count at all? Or is it sufficient to just record the increment (1)? + // -> I think storing only the increment is sufficient as we will define in the self-monitoring.yml file how we aggregate the '/self/action_count' measures + if (actionExecutionCounts == null) { + actionExecutionCounts = new HashMap<>(); + } + actionExecutionCounts.put(actionName, (actionExecutionCounts.containsKey(actionName) ? actionExecutionCounts.get(actionName) : 0) + 1); + + if (actionsSettings.getEnabled().getOrDefault(COUNT_METRIC_NAME, false)) { + recordMeasurement(COUNT_METRIC_NAME, 1L, customTags); + } + + } + + /** + * Records the measurement for the specific metric of an action. + * + * @param actionName The name of the action + * @param metricName The name of the metric + * @param value The value to be recorded + */ + private void recordMeasurement(String actionName, String metricName, long value) { + // return if the recorder or the metric is disabled + if (!isEnabled() || !env.getCurrentConfig() + .getSelfMonitoring() + .getActions() + .getEnabled() + .getOrDefault(metricName, false)) { + return; + } + // create custom tags + val customTags = new HashMap() {{ + put(ACTION_NAME_KEY, actionName); + }}; + // record measurement + selfMonitoringService.recordMeasurement(METRIC_NAME_PREFIX + metricName, value, customTags); + } + + /** + * Records the measurement for the metric. + * + * @param metricName The name of the metric + * @param value The value to be recorded + * @param customTags additional tags, which are added to the measurement + */ + private void recordMeasurement(String metricName, long value, HashMap customTags) { + selfMonitoringService.recordMeasurement(METRIC_NAME_PREFIX + metricName, value, customTags); + } + + @Override + protected boolean checkEnabledForConfig(InspectitConfig configuration) { + return configuration.getSelfMonitoring().isEnabled() && configuration.getSelfMonitoring() + .getActions() + .getEnabled() + .containsValue(true); + } + + @Override + protected boolean doEnable(InspectitConfig configuration) { + // TODO: do I need to check whether the InspectitConfig#action settings have been defined at all? + log.info("Enabling ActionMetricsRecorder."); + return true; + } + + @Override + protected boolean doDisable() { + log.info("Disabling ActionMetricsRecorder."); + // clear the map of actionStartTimes + actionStartTimesNanos.clear(); + return true; + } + + // probably useless code + + /** + * Records the start of the execution of an action. + * + * @param actionName The name of the action + */ + private void recordStart(String actionName) { + // do nothing if disabled + if (!isEnabled()) { + return; + } + + if (actionStartTimesNanos == null) { + actionStartTimesNanos = new HashMap<>(); + } + + actionStartTimesNanos.put(actionName, System.nanoTime()); + } + + /** + * Records the end of the execution of an action. + * + * @param actionName The name of the action + */ + private void recordEnd(String actionName) { + // do nothing if disabled + if (!isEnabled()) { + return; + } + + val actionStartTimeNanos = actionStartTimesNanos.get(actionName); + // if the ActionMetricsRecorder has been disabled during the execution of an action, the actionStartTimes has been cleared and no actionStartTime could be retrieved. + if (actionStartTimeNanos == null) { + return; + } + + // compute execution time in microseconds + val executionTimeMicros = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - actionStartTimeNanos); + + // remove the action from the start map + actionStartTimesNanos.remove(actionName); + + // record the execution time + record(actionName, executionTimeMicros); + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/IActionScope.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/IActionScope.java new file mode 100644 index 0000000000..498819d43e --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/IActionScope.java @@ -0,0 +1,24 @@ +package rocks.inspectit.ocelot.core.selfmonitoring; + +import io.opencensus.common.Scope; +import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; + +/** + * Scope for an {@link IHookAction} executed in the {@link rocks.inspectit.ocelot.core.instrumentation.hook.MethodHook} + */ +public interface IActionScope extends Scope { + + /** + * Starts the scope. + */ + public abstract void start(); + + /** + * Starts the scope and stores the given start time of the action + * + * @param startTimeNanos The start time of the action in nanoseconds. + */ + public abstract void start(long startTimeNanos); + +} + diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/LogMetricsRecorder.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/LogMetricsRecorder.java index 35ad76eeff..4a9471449a 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/LogMetricsRecorder.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/LogMetricsRecorder.java @@ -8,7 +8,7 @@ import java.util.Map; /** - * Logback recorder which exposes the the counts to the {@link SelfMonitoringService} + * Logback recorder which exposes the counts to the {@link SelfMonitoringService} */ @Component public class LogMetricsRecorder { diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/NoopActionScope.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/NoopActionScope.java new file mode 100644 index 0000000000..cad5f6b628 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/NoopActionScope.java @@ -0,0 +1,28 @@ +package rocks.inspectit.ocelot.core.selfmonitoring; + +/** + * Dummy no-op {@link IActionScope} + */ +public class NoopActionScope implements IActionScope { + + public static final NoopActionScope INSTANCE = new NoopActionScope(); + + private NoopActionScope(){ + + } + + @Override + public void start() { + + } + + @Override + public void start(long startTimeNanos) { + + } + + @Override + public void close() { + + } +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java new file mode 100644 index 0000000000..98ac80aef1 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java @@ -0,0 +1,54 @@ +package rocks.inspectit.ocelot.core.selfmonitoring; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import rocks.inspectit.ocelot.core.SpringTestBase; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests the configuration for {@link ActionsMetricsRecorder} + */ +public class ActionMetricsRecorderConfigTest extends SpringTestBase { + + @Autowired + private ActionsMetricsRecorder recorder; + + @Nested + class Defaults extends SpringTestBase { + + @Test + void checkDefaultEnabled() { + assertThat(recorder.isEnabled()).isTrue(); + } + } + + @Nested + class CheckConfigChanges extends SpringTestBase { + + @Test + @DirtiesContext + void checkExecutionTime() { + assertThat(recorder.isEnabled()).isTrue(); + updateProperties((mp) -> { + mp.setProperty("inspectit.selfMonitoring.actions.enabled.execution-time", "false"); + }); + assertThat(recorder.isEnabled()).isTrue(); + } + + @Test + @DirtiesContext + void checkAllDisabled() { + assertThat(recorder.isEnabled()).isTrue(); + updateProperties((mp) -> { + mp.setProperty("inspectit.selfMonitoring.actions.enabled.execution-time", "false"); + mp.setProperty("inspectit.selfMonitoring.actions.enabled.count", "false"); + }); + assertThat(recorder.isEnabled()).isFalse(); + } + } + +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderTest.java new file mode 100644 index 0000000000..aac5f9abf5 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderTest.java @@ -0,0 +1,120 @@ +package rocks.inspectit.ocelot.core.selfmonitoring; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import rocks.inspectit.ocelot.core.SpringTestBase; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +/** + * Tests the {@link rocks.inspectit.ocelot.core.selfmonitoring.ActionsMetricsRecorder} + */ +@ExtendWith(MockitoExtension.class) +public class ActionMetricsRecorderTest extends SpringTestBase { + + @Mock + private SelfMonitoringService selfMonitoringService; + + @Autowired + @InjectMocks + private ActionsMetricsRecorder recorder; + + @Nested + class ActionMetrics extends SpringTestBase { + + @BeforeEach + private void disableAllActionsMetrics() { + updateProperties((mp) -> { + mp.setProperty("inspectit.selfMonitoring.actions.enabled.count", "false"); + mp.setProperty("inspectit.selfMonitoring.actions.enabled.execution-time", "false"); + }); + assertThat(recorder.isEnabled()).isFalse(); + } + + @Test + @DirtiesContext + public void testExecutionIncrement() { + assertThat(recorder.isEnabled()).isFalse(); + + // make sure that the count/increment metrics is enabled. + updateProperties((mp) -> { + mp.setProperty("inspectit.selfMonitoring.actions.enabled.count", "true"); + }); + assertThat(recorder.isEnabled()).isTrue(); + + // record fake measurement + recorder.record("my-action", 1000L); + + // verify that the increment has been recorded + verify(selfMonitoringService, times(1)).recordMeasurement(anyString(), eq(1L), anyMap()); + + // verify that no other unverified interactions are left + verifyNoMoreInteractions(selfMonitoringService); + } + + @Test + @DirtiesContext + public void testExecutionTime() { + assertThat(recorder.isEnabled()).isFalse(); + + // make sure that the execution time metrics is enabled + updateProperties((mp) -> { + mp.setProperty("inspectit.selfMonitoring.actions.enabled.execution-time", "true"); + }); + + assertThat(recorder.isEnabled()).isTrue(); + + // record fake measurement + recorder.record("my-action", 1000L); + + // verify that the execution time has been recorded + verify(selfMonitoringService, times(1)).recordMeasurement(anyString(), eq(1000L), anyMap()); + + // verify that no other unverified interactions are left + verifyNoMoreInteractions(selfMonitoringService); + } + + @Test + @DirtiesContext + public void testAllMetrics() { + assertThat(recorder.isEnabled()).isFalse(); + // enable all metrics + enableAllMetrics(); + // verify the recorder is enabled + assertThat(recorder.isEnabled()).isTrue(); + + // record fake measurement + recorder.record("my-action", 1000L); + + // verify increment + verify(selfMonitoringService, times(1)).recordMeasurement(anyString(), eq(1L), anyMap()); + + // verify execution time + verify(selfMonitoringService, times(1)).recordMeasurement(anyString(), eq(1000L), anyMap()); + + // verify that no other unverified interactions are left + verifyNoMoreInteractions(selfMonitoringService); + } + + /** + * Enables all metrics for the {@link ActionsMetricsRecorder} + */ + private void enableAllMetrics() { + // enable all metrics. + updateProperties((mp) -> { + mp.setProperty("inspectit.selfMonitoring.actions.enabled.execution-time", "true"); + mp.setProperty("inspectit.selfMonitoring.actions.enabled.count", "true"); + }); + } + + } + +} diff --git a/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md b/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md index 88f427a978..7ddf9046dc 100644 --- a/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md +++ b/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md @@ -15,6 +15,8 @@ The metric is split by the tag containing the component name and also includes a |```inspectit/self/duration```|us|The total time spent by inspectIT doing internal tasks, such as configuration loading, instrumenting, etc.The metric contains the tag ```component_name```, specifying in which component the time was spent |```inspectit/self/instrumentation-queue-size```|`classes`|InspectIT applies the configured instrumentation by working through a queue of classes it has to analyze and potentially instrument. This metric exposes the current size of this queue. By comparing it against the [total number of loaded classes](metrics/metric-recorders.md#class-loading-metrics), the instrumentation progress can be estimated. |```inspectit/self/instrumented-classes```|`classes`|Exposes the total number of classes which are currently instrumented by inspectIT. +|```inspectit/self/actions/execution-time```|us|The execution time of individual actions. The metric contains the tag `action_name`, specifying the name of the instrumented action. +|```inspectit/self/actions/count```|`action executions`|The number of executions per action. The metric contains the tag `action_name`, specifying the name of the instrumented action. Self monitoring is enabled by default and can be disabled by setting the `inspectit.self-monitoring.enabled` property to `false`. From 2439161abc8dab5dd0e0af13637b0db82ed33db1 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 16 Nov 2021 16:58:00 +0100 Subject: [PATCH 02/86] Removed unused code from ActionsMetricsRecorder --- .../ActionsMetricsRecorder.java | 71 +------------------ 1 file changed, 2 insertions(+), 69 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java index 3dde7b45c2..f6aa2c7156 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java @@ -10,7 +10,6 @@ import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; import java.util.HashMap; -import java.util.concurrent.TimeUnit; /** * Recorder for {@link MethodHook} to record and expose metrics (e.g., number of invocation, execution time) of individual {@link rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction}. @@ -42,16 +41,6 @@ public class ActionsMetricsRecorder extends DynamicallyActivatableService { */ private static final String ACTION_NAME_KEY = "action_name"; - /** - * The map containing the actions' start time in nanoseconds. - */ - private HashMap actionStartTimesNanos = new HashMap<>(); - - /** - * The map containing the actions' execution counts. - */ - private HashMap actionExecutionCounts = new HashMap<>(); - public ActionsMetricsRecorder() { // TODO: do metrics need to be enabled in order to take self-monitoring measurements? super("metrics.enabled", "selfMonitoring.actions"); @@ -62,7 +51,7 @@ public ActionsMetricsRecorder() { * * @param action The action * @param executionTimeMicros The execution time in microseconds - * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? + * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? */ public void record(IHookAction action, long executionTimeMicros) { record(action.getName(), executionTimeMicros); @@ -73,7 +62,7 @@ public void record(IHookAction action, long executionTimeMicros) { * * @param actionName The name of the execution * @param executionTimeMicros The execution time in microseconds - * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? + * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? */ public void record(String actionName, long executionTimeMicros) { @@ -97,13 +86,6 @@ public void record(String actionName, long executionTimeMicros) { } // record the execution count increment - // TODO: do I need to store the count at all? Or is it sufficient to just record the increment (1)? - // -> I think storing only the increment is sufficient as we will define in the self-monitoring.yml file how we aggregate the '/self/action_count' measures - if (actionExecutionCounts == null) { - actionExecutionCounts = new HashMap<>(); - } - actionExecutionCounts.put(actionName, (actionExecutionCounts.containsKey(actionName) ? actionExecutionCounts.get(actionName) : 0) + 1); - if (actionsSettings.getEnabled().getOrDefault(COUNT_METRIC_NAME, false)) { recordMeasurement(COUNT_METRIC_NAME, 1L, customTags); } @@ -163,55 +145,6 @@ protected boolean doEnable(InspectitConfig configuration) { @Override protected boolean doDisable() { log.info("Disabling ActionMetricsRecorder."); - // clear the map of actionStartTimes - actionStartTimesNanos.clear(); return true; } - - // probably useless code - - /** - * Records the start of the execution of an action. - * - * @param actionName The name of the action - */ - private void recordStart(String actionName) { - // do nothing if disabled - if (!isEnabled()) { - return; - } - - if (actionStartTimesNanos == null) { - actionStartTimesNanos = new HashMap<>(); - } - - actionStartTimesNanos.put(actionName, System.nanoTime()); - } - - /** - * Records the end of the execution of an action. - * - * @param actionName The name of the action - */ - private void recordEnd(String actionName) { - // do nothing if disabled - if (!isEnabled()) { - return; - } - - val actionStartTimeNanos = actionStartTimesNanos.get(actionName); - // if the ActionMetricsRecorder has been disabled during the execution of an action, the actionStartTimes has been cleared and no actionStartTime could be retrieved. - if (actionStartTimeNanos == null) { - return; - } - - // compute execution time in microseconds - val executionTimeMicros = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - actionStartTimeNanos); - - // remove the action from the start map - actionStartTimesNanos.remove(actionName); - - // record the execution time - record(actionName, executionTimeMicros); - } } From cae483cfeb42d4d613e7bd25b643dd424c234dcc Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 18 Nov 2021 11:15:23 +0100 Subject: [PATCH 03/86] The state of the ActionsMetricsRecorder now also depends on the master switch metrics.enabled --- .../core/selfmonitoring/ActionsMetricsRecorder.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java index f6aa2c7156..0dea3ebc9a 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java @@ -42,7 +42,6 @@ public class ActionsMetricsRecorder extends DynamicallyActivatableService { private static final String ACTION_NAME_KEY = "action_name"; public ActionsMetricsRecorder() { - // TODO: do metrics need to be enabled in order to take self-monitoring measurements? super("metrics.enabled", "selfMonitoring.actions"); } @@ -51,7 +50,7 @@ public ActionsMetricsRecorder() { * * @param action The action * @param executionTimeMicros The execution time in microseconds - * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? + * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? */ public void record(IHookAction action, long executionTimeMicros) { record(action.getName(), executionTimeMicros); @@ -62,7 +61,7 @@ public void record(IHookAction action, long executionTimeMicros) { * * @param actionName The name of the execution * @param executionTimeMicros The execution time in microseconds - * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? + * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? */ public void record(String actionName, long executionTimeMicros) { @@ -129,10 +128,9 @@ private void recordMeasurement(String metricName, long value, HashMap Date: Thu, 18 Nov 2021 17:07:54 +0100 Subject: [PATCH 04/86] improved implementation of ActionMetricsRecorder - renamed "actions" to "action" (singular instead of plural) - only the "inspectit/self/action/execution-time" is recorded. The execution count is computed as a COUNT aggregation in the view - fixed the ActionRecorderTestAgent to test whether the metrics are recorded properly with IActionHooks - changed the settings for ActionMetricsRecorder to ActionMetricsRecorderSettings (new class) - changed some "val" to typed variables --- .../ActionMetricsRecorderTestAgent.java | 31 ++++++++++--- .../config/ActionMetricsRecorderTest.yml | 28 +++++++++++ .../selfmonitoring/ActionMetricsSettings.java | 15 ++++++ .../SelfMonitoringSettings.java | 4 +- .../ocelot/config/default/self-monitoring.yml | 26 ++++------- .../core/instrumentation/hook/MethodHook.java | 26 +++++------ .../hook/MethodHookGenerator.java | 6 +-- ...corder.java => ActionMetricsRecorder.java} | 46 ++++++++----------- .../selfmonitoring/ActionScopeFactory.java | 9 ++-- .../core/selfmonitoring/ActionScopeImpl.java | 14 +++--- .../ActionMetricsRecorderConfigTest.java | 12 ++--- .../ActionMetricsRecorderTest.java | 37 +++------------ .../docs/metrics/self-monitoring.md | 4 +- 13 files changed, 135 insertions(+), 123 deletions(-) create mode 100644 inspectit-ocelot-agent/src/system-test/resources/config/ActionMetricsRecorderTest.yml create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/ActionMetricsSettings.java rename inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/{ActionsMetricsRecorder.java => ActionMetricsRecorder.java} (77%) diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderTestAgent.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderTestAgent.java index 65c96b748b..bcebd8521d 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderTestAgent.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderTestAgent.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import rocks.inspectit.ocelot.metrics.MetricsSysTestBase; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -19,19 +20,26 @@ public class ActionMetricsRecorderTestAgent extends MetricsSysTestBase { private static final ViewManager viewManager = Stats.getViewManager(); + public void trigger() { + System.out.println("trigger method"); + } + @Test public void testActionsMetricsRecorder() { await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { - // TODO fire some fake action + // get the views + ViewData actionExecutionTimes = viewManager.getView(View.Name.create("inspectit/self/action/execution-time")); + ViewData actionExecutionCounts = viewManager.getView(View.Name.create("inspectit/self/action/count")); - ViewData actionExecutionTimes = viewManager.getView(View.Name.create("inspectit/self/actions/execution-time")); - ViewData actionExecutionCounts = viewManager.getView(View.Name.create("inspectit/self/actions/count")); - - // fake record some measurements + // record some measurements (that have been specified in ActionMetricsRecorderTest.yml) + trigger(); + // verify that execution-time and count has been recorded assertThat(actionExecutionTimes).isNotNull(); assertThat(actionExecutionTimes.getAggregationMap()).isNotNull().isNotEmpty(); + assertThat(actionExecutionCounts).isNotNull(); + assertThat(actionExecutionCounts.getAggregationMap()).isNotNull().isNotEmpty(); Map.Entry, AggregationData> executionTime = actionExecutionTimes.getAggregationMap() .entrySet() @@ -39,15 +47,24 @@ public void testActionsMetricsRecorder() { .findFirst() .get(); + Map.Entry, AggregationData> executionCount = actionExecutionCounts.getAggregationMap() + .entrySet() + .stream() + .findFirst() + .get(); + // ensure that tags are present assertThat(executionTime.getKey()).isNotEmpty(); + assertThat(executionCount.getKey()).isNotEmpty(); System.out.println("print keys"); - System.out.println(executionTime.getKey().toArray()); + System.out.println(Arrays.toString(executionTime.getKey().toArray())); // ensure sanity of values - long executionTimeVal = ((AggregationData.LastValueDataLong) executionTime.getValue()).getLastValue(); + long executionTimeVal = ((AggregationData.SumDataLong) executionTime.getValue()).getSum(); assertThat(executionTimeVal).isGreaterThan(0); + long executionCountVal = ((AggregationData.CountData) executionCount.getValue()).getCount(); + assertThat(executionCountVal).isGreaterThan(0); }); } diff --git a/inspectit-ocelot-agent/src/system-test/resources/config/ActionMetricsRecorderTest.yml b/inspectit-ocelot-agent/src/system-test/resources/config/ActionMetricsRecorderTest.yml new file mode 100644 index 0000000000..d896e1e41c --- /dev/null +++ b/inspectit-ocelot-agent/src/system-test/resources/config/ActionMetricsRecorderTest.yml @@ -0,0 +1,28 @@ +# dummy scopes, actions, and rules for the ActionMetricsRecorderTestAgent + +inspectit: + instrumentation: + scopes: + + # the trigger() method in ActionMetricsRecorderTestAgent + 's_actionmetrics_trigger': + type: + name: 'rocks.inspectit.ocelot.metrics.selfmonitoring.ActionMetricsRecorderTestAgent' + methods: + - name: 'trigger' + + actions: + + # simple action to get the full qualified class name + 'a_trigger_action': + input: + _class: 'Class' + value: '_class.getName()' + + rules: + 'r_...': + scopes: + 's_actionmetrics_trigger': true + entry: + 'c_dummy': + action: 'a_trigger_action' \ No newline at end of file diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/ActionMetricsSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/ActionMetricsSettings.java new file mode 100644 index 0000000000..67e6fb7a73 --- /dev/null +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/ActionMetricsSettings.java @@ -0,0 +1,15 @@ +package rocks.inspectit.ocelot.config.model.selfmonitoring; + +import lombok.Data; + +/** + * Settings for the {@link rocks.inspectit.ocelot.core.selfmonitoring.ActionsMetricsRecorder} + */ +@Data +public class ActionMetricsSettings { + + /** + * Whether metrics of actions (e.g., execution time) are recorded + */ + private boolean enabled; +} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/SelfMonitoringSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/SelfMonitoringSettings.java index 6fcb81e3a3..d929fb4476 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/SelfMonitoringSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/selfmonitoring/SelfMonitoringSettings.java @@ -15,10 +15,10 @@ public class SelfMonitoringSettings { */ private boolean enabled; - /** + /** * Settings for {@link rocks.inspectit.ocelot.core.selfmonitoring.ActionsMetricsRecorder} */ @Valid - private StandardMetricsSettings actions; + private ActionMetricsSettings actionMetrics; } diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml index 5ee8154587..bb7fb339bd 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml @@ -5,12 +5,9 @@ inspectit: enabled: true # settings regarding the capturing of action related metrics - actions: - enabled: - # if true, the execution time (duration in ms) per action will be recorded - execution-time: true - # if true, the number of executions per action will be recorded - count: true + actionMetrics: + # if true, the execution time (duration in ms) per action will be recorded + enabled: true metrics: definitions: @@ -45,22 +42,17 @@ inspectit: aggregation: SUM tags: {"level": true} - '[inspectit/self/actions/execution-time]': - enabled: ${inspectit.self-monitoring.actions.enabled.execution-time} + '[inspectit/self/action/execution-time]': + enabled: ${inspectit.self-monitoring.actionMetrics.enabled} type: LONG unit: us description: "the execution time of the action" views: - '[inspectit/self/actions/execution-time]': + '[inspectit/self/action/execution-time]': + aggregation: SUM tags: action_name: true - '[inspectit/self/actions/count]': - enabled: ${inspectit.self-monitoring.actions.enabled.count} - type: LONG - unit: action executions - description: "number of action executions" - views: - '[inspectit/self/actions/count]': - aggregation: SUM + '[inspectit/self/action/count]': + aggregation: COUNT tags: action_name: true diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java index e3fe1a000c..cae9b48506 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java @@ -4,15 +4,15 @@ import lombok.Singular; import lombok.Value; import lombok.extern.slf4j.Slf4j; -import lombok.val; import rocks.inspectit.ocelot.bootstrap.context.InternalInspectitContext; import rocks.inspectit.ocelot.bootstrap.instrumentation.IMethodHook; import rocks.inspectit.ocelot.core.instrumentation.config.model.MethodHookConfiguration; import rocks.inspectit.ocelot.core.instrumentation.context.ContextManager; import rocks.inspectit.ocelot.core.instrumentation.context.InspectitContextImpl; import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; +import rocks.inspectit.ocelot.core.selfmonitoring.ActionMetricsRecorder; import rocks.inspectit.ocelot.core.selfmonitoring.ActionScopeFactory; -import rocks.inspectit.ocelot.core.selfmonitoring.ActionsMetricsRecorder; +import rocks.inspectit.ocelot.core.selfmonitoring.IActionScope; import java.util.ArrayList; import java.util.List; @@ -68,7 +68,7 @@ public class MethodHook implements IMethodHook { /** * The metrics recorder for the {@link IHookAction} */ - private final ActionsMetricsRecorder actionsMetricsRecorder; + private final ActionMetricsRecorder actionMetricsRecorder; /** * The factory that creates/spawns new scopes for an {@link IHookAction} @@ -76,7 +76,7 @@ public class MethodHook implements IMethodHook { private final ActionScopeFactory actionScopeFactory; @Builder - public MethodHook(MethodHookConfiguration sourceConfiguration, ContextManager inspectitContextManager, @Singular List entryActions, @Singular List exitActions, MethodReflectionInformation methodInformation, ActionsMetricsRecorder actionsMetricsRecorder, ActionScopeFactory actionScopeFactory) { + public MethodHook(MethodHookConfiguration sourceConfiguration, ContextManager inspectitContextManager, @Singular List entryActions, @Singular List exitActions, MethodReflectionInformation methodInformation, ActionMetricsRecorder actionMetricsRecorder, ActionScopeFactory actionScopeFactory) { this.sourceConfiguration = sourceConfiguration; this.inspectitContextManager = inspectitContextManager; this.entryActions = new ArrayList<>(entryActions); @@ -84,17 +84,17 @@ public MethodHook(MethodHookConfiguration sourceConfiguration, ContextManager in this.exitActions = new ArrayList<>(exitActions); activeExitActions = new CopyOnWriteArrayList<>(exitActions); this.methodInformation = methodInformation; - this.actionsMetricsRecorder = actionsMetricsRecorder; + this.actionMetricsRecorder = actionMetricsRecorder; this.actionScopeFactory = actionScopeFactory; } @Override public InternalInspectitContext onEnter(Object[] args, Object thiz) { - val inspectitContext = inspectitContextManager.enterNewContext(); - val executionContext = new IHookAction.ExecutionContext(args, thiz, null, null, this, inspectitContext); + InspectitContextImpl inspectitContext = inspectitContextManager.enterNewContext(); + IHookAction.ExecutionContext executionContext = new IHookAction.ExecutionContext(args, thiz, null, null, this, inspectitContext); - for (val action : activeEntryActions) { - try (val scope = actionScopeFactory.getScope(action)) { + for (IHookAction action : activeEntryActions) { + try (IActionScope scope = actionScopeFactory.getScope(action)) { action.execute(executionContext); } catch (Throwable t) { log.error("Entry action {} executed for method {} threw an exception and from now on is disabled!", action.getName(), methodInformation.getMethodFQN(), t); @@ -108,9 +108,9 @@ public InternalInspectitContext onEnter(Object[] args, Object thiz) { @Override public void onExit(Object[] args, Object thiz, Object returnValue, Throwable thrown, InternalInspectitContext context) { - val executionContext = new IHookAction.ExecutionContext(args, thiz, returnValue, thrown, this, (InspectitContextImpl) context); - for (val action : activeExitActions) { - try (val scope = actionScopeFactory.getScope(action)) { + IHookAction.ExecutionContext executionContext = new IHookAction.ExecutionContext(args, thiz, returnValue, thrown, this, (InspectitContextImpl) context); + for (IHookAction action : activeExitActions) { + try (IActionScope scope = actionScopeFactory.getScope(action)) { action.execute(executionContext); } catch (Throwable t) { log.error("Exit action {} executed for method {} threw an exception and from now on is disabled!", action.getName(), methodInformation.getMethodFQN(), t); @@ -124,7 +124,7 @@ public void onExit(Object[] args, Object thiz, Object returnValue, Throwable thr * @return An exact copy of this method hook but with all deactivated actions reactivated. */ public MethodHook getResettedCopy() { - return new MethodHook(sourceConfiguration, inspectitContextManager, entryActions, exitActions, methodInformation, actionsMetricsRecorder, actionScopeFactory); + return new MethodHook(sourceConfiguration, inspectitContextManager, entryActions, exitActions, methodInformation, actionMetricsRecorder, actionScopeFactory); } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java index 1fb00e491e..c71bdf7267 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java @@ -23,8 +23,8 @@ import rocks.inspectit.ocelot.core.instrumentation.hook.tags.CommonTagsToAttributesManager; import rocks.inspectit.ocelot.core.metrics.MeasuresAndViewsManager; import rocks.inspectit.ocelot.core.privacy.obfuscation.ObfuscationManager; +import rocks.inspectit.ocelot.core.selfmonitoring.ActionMetricsRecorder; import rocks.inspectit.ocelot.core.selfmonitoring.ActionScopeFactory; -import rocks.inspectit.ocelot.core.selfmonitoring.ActionsMetricsRecorder; import rocks.inspectit.ocelot.core.tags.CommonTagsManager; import java.util.*; @@ -65,7 +65,7 @@ public class MethodHookGenerator { private StackTraceSampler stackTraceSampler; @Autowired - private ActionsMetricsRecorder actionsMetricsRecorder; + private ActionMetricsRecorder actionMetricsRecorder; @Autowired private ActionScopeFactory actionScopeFactory; @@ -104,7 +104,7 @@ public MethodHook buildHook(Class declaringClass, MethodDescription method, M buildMetricsRecorder(config).ifPresent(builder::exitAction); builder.exitActions(buildActionCalls(config.getPostExitActions(), methodInfo)); - builder.actionsMetricsRecorder(actionsMetricsRecorder); + builder.actionMetricsRecorder(actionMetricsRecorder); builder.actionScopeFactory(actionScopeFactory); return builder.build(); diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorder.java similarity index 77% rename from inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java rename to inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorder.java index 0dea3ebc9a..0ee3b0012e 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionsMetricsRecorder.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorder.java @@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.selfmonitoring.ActionMetricsSettings; import rocks.inspectit.ocelot.core.instrumentation.hook.MethodHook; import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; @@ -16,7 +17,7 @@ */ @Component @Slf4j -public class ActionsMetricsRecorder extends DynamicallyActivatableService { +public class ActionMetricsRecorder extends DynamicallyActivatableService { @Autowired private SelfMonitoringService selfMonitoringService; @@ -24,25 +25,20 @@ public class ActionsMetricsRecorder extends DynamicallyActivatableService { /** * The prefix of the recorded metrics. */ - private static final String METRIC_NAME_PREFIX = "actions/"; + private static final String METRIC_NAME_PREFIX = "action/"; /** * The metric name for the execution time of an action. */ private static final String EXECUTION_TIME_METRIC_NAME = "execution-time"; - /** - * The metric name for execution count. - */ - private static final String COUNT_METRIC_NAME = "count"; - /** * The key of the action's name used in custom tags. */ private static final String ACTION_NAME_KEY = "action_name"; - public ActionsMetricsRecorder() { - super("metrics.enabled", "selfMonitoring.actions"); + public ActionMetricsRecorder() { + super("metrics.enabled", "selfMonitoring.actionMetrics"); } /** @@ -50,7 +46,7 @@ public ActionsMetricsRecorder() { * * @param action The action * @param executionTimeMicros The execution time in microseconds - * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? + * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? */ public void record(IHookAction action, long executionTimeMicros) { record(action.getName(), executionTimeMicros); @@ -61,7 +57,7 @@ public void record(IHookAction action, long executionTimeMicros) { * * @param actionName The name of the execution * @param executionTimeMicros The execution time in microseconds - * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? + * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? */ public void record(String actionName, long executionTimeMicros) { @@ -70,24 +66,22 @@ public void record(String actionName, long executionTimeMicros) { return; } - // log.info("recording action '{}', executionTimeMicros = {}", actionName, executionTimeMicros); - - val actionsSettings = env.getCurrentConfig().getSelfMonitoring().getActions(); + log.info("recording action '{}', executionTimeMicros = {}", actionName, executionTimeMicros); // create custom tags - val customTags = new HashMap() {{ + HashMap customTags = new HashMap() {{ put(ACTION_NAME_KEY, actionName); }}; // record the action's execution time if enabled - if (actionsSettings.getEnabled().getOrDefault(EXECUTION_TIME_METRIC_NAME, false)) { - recordMeasurement(EXECUTION_TIME_METRIC_NAME, executionTimeMicros, customTags); - } + recordMeasurement(EXECUTION_TIME_METRIC_NAME, executionTimeMicros, customTags); - // record the execution count increment - if (actionsSettings.getEnabled().getOrDefault(COUNT_METRIC_NAME, false)) { - recordMeasurement(COUNT_METRIC_NAME, 1L, customTags); - } + // if we later have different metrics that can be individually turned on or off, we need to check via ActionMetricsSettings what to record. + + // ActionMetricsSettings actionsSettings = env.getCurrentConfig().getSelfMonitoring().getActionMetrics(); + // if (actionsSettings.isEnabled()) { + // recordMeasurement(EXECUTION_TIME_METRIC_NAME, executionTimeMicros, customTags); + // } } @@ -100,11 +94,7 @@ public void record(String actionName, long executionTimeMicros) { */ private void recordMeasurement(String actionName, String metricName, long value) { // return if the recorder or the metric is disabled - if (!isEnabled() || !env.getCurrentConfig() - .getSelfMonitoring() - .getActions() - .getEnabled() - .getOrDefault(metricName, false)) { + if (!isEnabled()) { return; } // create custom tags @@ -130,7 +120,7 @@ private void recordMeasurement(String metricName, long value, HashMap { - mp.setProperty("inspectit.selfMonitoring.actions.enabled.execution-time", "false"); + mp.setProperty("inspectit.selfMonitoring.actionMetrics.enabled", "true"); }); assertThat(recorder.isEnabled()).isTrue(); } @@ -44,8 +43,7 @@ void checkExecutionTime() { void checkAllDisabled() { assertThat(recorder.isEnabled()).isTrue(); updateProperties((mp) -> { - mp.setProperty("inspectit.selfMonitoring.actions.enabled.execution-time", "false"); - mp.setProperty("inspectit.selfMonitoring.actions.enabled.count", "false"); + mp.setProperty("inspectit.selfMonitoring.actionMetrics.enabled", "false"); }); assertThat(recorder.isEnabled()).isFalse(); } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderTest.java index aac5f9abf5..4d7ed43463 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderTest.java @@ -15,7 +15,7 @@ import static org.mockito.Mockito.*; /** - * Tests the {@link rocks.inspectit.ocelot.core.selfmonitoring.ActionsMetricsRecorder} + * Tests the {@link ActionMetricsRecorder} */ @ExtendWith(MockitoExtension.class) public class ActionMetricsRecorderTest extends SpringTestBase { @@ -25,7 +25,7 @@ public class ActionMetricsRecorderTest extends SpringTestBase { @Autowired @InjectMocks - private ActionsMetricsRecorder recorder; + private ActionMetricsRecorder recorder; @Nested class ActionMetrics extends SpringTestBase { @@ -33,32 +33,11 @@ class ActionMetrics extends SpringTestBase { @BeforeEach private void disableAllActionsMetrics() { updateProperties((mp) -> { - mp.setProperty("inspectit.selfMonitoring.actions.enabled.count", "false"); - mp.setProperty("inspectit.selfMonitoring.actions.enabled.execution-time", "false"); + mp.setProperty("inspectit.selfMonitoring.actionMetrics.enabled", "false"); }); assertThat(recorder.isEnabled()).isFalse(); } - @Test - @DirtiesContext - public void testExecutionIncrement() { - assertThat(recorder.isEnabled()).isFalse(); - - // make sure that the count/increment metrics is enabled. - updateProperties((mp) -> { - mp.setProperty("inspectit.selfMonitoring.actions.enabled.count", "true"); - }); - assertThat(recorder.isEnabled()).isTrue(); - - // record fake measurement - recorder.record("my-action", 1000L); - - // verify that the increment has been recorded - verify(selfMonitoringService, times(1)).recordMeasurement(anyString(), eq(1L), anyMap()); - - // verify that no other unverified interactions are left - verifyNoMoreInteractions(selfMonitoringService); - } @Test @DirtiesContext @@ -67,7 +46,7 @@ public void testExecutionTime() { // make sure that the execution time metrics is enabled updateProperties((mp) -> { - mp.setProperty("inspectit.selfMonitoring.actions.enabled.execution-time", "true"); + mp.setProperty("inspectit.selfMonitoring.actionMetrics.enabled", "true"); }); assertThat(recorder.isEnabled()).isTrue(); @@ -94,9 +73,6 @@ public void testAllMetrics() { // record fake measurement recorder.record("my-action", 1000L); - // verify increment - verify(selfMonitoringService, times(1)).recordMeasurement(anyString(), eq(1L), anyMap()); - // verify execution time verify(selfMonitoringService, times(1)).recordMeasurement(anyString(), eq(1000L), anyMap()); @@ -105,13 +81,12 @@ public void testAllMetrics() { } /** - * Enables all metrics for the {@link ActionsMetricsRecorder} + * Enables all metrics for the {@link ActionMetricsRecorder} */ private void enableAllMetrics() { // enable all metrics. updateProperties((mp) -> { - mp.setProperty("inspectit.selfMonitoring.actions.enabled.execution-time", "true"); - mp.setProperty("inspectit.selfMonitoring.actions.enabled.count", "true"); + mp.setProperty("inspectit.selfMonitoring.actionMetrics.enabled", "true"); }); } diff --git a/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md b/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md index 7ddf9046dc..1865eeafb5 100644 --- a/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md +++ b/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md @@ -15,8 +15,8 @@ The metric is split by the tag containing the component name and also includes a |```inspectit/self/duration```|us|The total time spent by inspectIT doing internal tasks, such as configuration loading, instrumenting, etc.The metric contains the tag ```component_name```, specifying in which component the time was spent |```inspectit/self/instrumentation-queue-size```|`classes`|InspectIT applies the configured instrumentation by working through a queue of classes it has to analyze and potentially instrument. This metric exposes the current size of this queue. By comparing it against the [total number of loaded classes](metrics/metric-recorders.md#class-loading-metrics), the instrumentation progress can be estimated. |```inspectit/self/instrumented-classes```|`classes`|Exposes the total number of classes which are currently instrumented by inspectIT. -|```inspectit/self/actions/execution-time```|us|The execution time of individual actions. The metric contains the tag `action_name`, specifying the name of the instrumented action. -|```inspectit/self/actions/count```|`action executions`|The number of executions per action. The metric contains the tag `action_name`, specifying the name of the instrumented action. +|```inspectit/self/action/execution-time```|us|The execution time of individual actions. The metric contains the tag `action_name`, specifying the name of the instrumented action. +|```inspectit/self/action/count```|`action executions`|The number of executions per action. The metric contains the tag `action_name`, specifying the name of the instrumented action. Self monitoring is enabled by default and can be disabled by setting the `inspectit.self-monitoring.enabled` property to `false`. From 653f7175c780e264116b22e94212a033534258c6 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 19 Nov 2021 15:26:03 +0100 Subject: [PATCH 05/86] Removed log statements --- .../ocelot/core/selfmonitoring/ActionMetricsRecorder.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorder.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorder.java index 0ee3b0012e..c86cbe5f49 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorder.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorder.java @@ -46,7 +46,7 @@ public ActionMetricsRecorder() { * * @param action The action * @param executionTimeMicros The execution time in microseconds - * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? + * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? */ public void record(IHookAction action, long executionTimeMicros) { record(action.getName(), executionTimeMicros); @@ -57,7 +57,7 @@ public void record(IHookAction action, long executionTimeMicros) { * * @param actionName The name of the execution * @param executionTimeMicros The execution time in microseconds - * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? + * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? */ public void record(String actionName, long executionTimeMicros) { @@ -66,8 +66,6 @@ public void record(String actionName, long executionTimeMicros) { return; } - log.info("recording action '{}', executionTimeMicros = {}", actionName, executionTimeMicros); - // create custom tags HashMap customTags = new HashMap() {{ put(ACTION_NAME_KEY, actionName); @@ -76,7 +74,7 @@ public void record(String actionName, long executionTimeMicros) { // record the action's execution time if enabled recordMeasurement(EXECUTION_TIME_METRIC_NAME, executionTimeMicros, customTags); - // if we later have different metrics that can be individually turned on or off, we need to check via ActionMetricsSettings what to record. + // if we later have different metrics that can be individually turned on or off, we need to check via ActionMetricsSettings what to record, e.g., // ActionMetricsSettings actionsSettings = env.getCurrentConfig().getSelfMonitoring().getActionMetrics(); // if (actionsSettings.isEnabled()) { From a10487a697fdce56b21c3a4573b9a5bb2b3e8cb8 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 25 Nov 2021 08:54:23 +0100 Subject: [PATCH 06/86] Fixed broken tests in MethodHookTest and MethodHoodGeneratorTest. Removed unused reference to ActionMetricsRecorder in MethodHook and MethodHookGenerator. --- .../core/instrumentation/hook/MethodHook.java | 14 +++++++------- .../instrumentation/hook/MethodHookGenerator.java | 4 ---- .../hook/MethodHookGeneratorTest.java | 4 ++++ .../core/instrumentation/hook/MethodHookTest.java | 8 ++++++++ 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java index cae9b48506..4b83e8c99f 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java @@ -14,6 +14,7 @@ import rocks.inspectit.ocelot.core.selfmonitoring.ActionScopeFactory; import rocks.inspectit.ocelot.core.selfmonitoring.IActionScope; +import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -65,18 +66,15 @@ public class MethodHook implements IMethodHook { */ private final MethodReflectionInformation methodInformation; - /** - * The metrics recorder for the {@link IHookAction} - */ - private final ActionMetricsRecorder actionMetricsRecorder; /** * The factory that creates/spawns new scopes for an {@link IHookAction} */ + @NotNull private final ActionScopeFactory actionScopeFactory; @Builder - public MethodHook(MethodHookConfiguration sourceConfiguration, ContextManager inspectitContextManager, @Singular List entryActions, @Singular List exitActions, MethodReflectionInformation methodInformation, ActionMetricsRecorder actionMetricsRecorder, ActionScopeFactory actionScopeFactory) { + public MethodHook(MethodHookConfiguration sourceConfiguration, ContextManager inspectitContextManager, @Singular List entryActions, @Singular List exitActions, MethodReflectionInformation methodInformation, ActionScopeFactory actionScopeFactory) { this.sourceConfiguration = sourceConfiguration; this.inspectitContextManager = inspectitContextManager; this.entryActions = new ArrayList<>(entryActions); @@ -84,7 +82,9 @@ public MethodHook(MethodHookConfiguration sourceConfiguration, ContextManager in this.exitActions = new ArrayList<>(exitActions); activeExitActions = new CopyOnWriteArrayList<>(exitActions); this.methodInformation = methodInformation; - this.actionMetricsRecorder = actionMetricsRecorder; + if (actionScopeFactory == null) { + throw new IllegalArgumentException("ActionScopeFactory must not be null!"); + } this.actionScopeFactory = actionScopeFactory; } @@ -124,7 +124,7 @@ public void onExit(Object[] args, Object thiz, Object returnValue, Throwable thr * @return An exact copy of this method hook but with all deactivated actions reactivated. */ public MethodHook getResettedCopy() { - return new MethodHook(sourceConfiguration, inspectitContextManager, entryActions, exitActions, methodInformation, actionMetricsRecorder, actionScopeFactory); + return new MethodHook(sourceConfiguration, inspectitContextManager, entryActions, exitActions, methodInformation, actionScopeFactory); } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java index c71bdf7267..0bd80c9b2c 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java @@ -64,9 +64,6 @@ public class MethodHookGenerator { @Autowired private StackTraceSampler stackTraceSampler; - @Autowired - private ActionMetricsRecorder actionMetricsRecorder; - @Autowired private ActionScopeFactory actionScopeFactory; @@ -104,7 +101,6 @@ public MethodHook buildHook(Class declaringClass, MethodDescription method, M buildMetricsRecorder(config).ifPresent(builder::exitAction); builder.exitActions(buildActionCalls(config.getPostExitActions(), methodInfo)); - builder.actionMetricsRecorder(actionMetricsRecorder); builder.actionScopeFactory(actionScopeFactory); return builder.build(); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGeneratorTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGeneratorTest.java index fb41d739c5..36ac188b1f 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGeneratorTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGeneratorTest.java @@ -20,6 +20,7 @@ import rocks.inspectit.ocelot.core.instrumentation.hook.actions.span.SetSpanStatusAction; import rocks.inspectit.ocelot.core.instrumentation.hook.actions.span.WriteSpanAttributesAction; import rocks.inspectit.ocelot.core.privacy.obfuscation.ObfuscationManager; +import rocks.inspectit.ocelot.core.selfmonitoring.ActionScopeFactory; import rocks.inspectit.ocelot.core.testutils.Dummy; import java.util.Collections; @@ -35,6 +36,9 @@ public class MethodHookGeneratorTest { @Mock ContextManager contextManager; + @Mock + ActionScopeFactory actionScopeFactory; + @InjectMocks MethodHookGenerator generator; diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookTest.java index 776868b49b..206be4b117 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookTest.java @@ -11,6 +11,7 @@ import rocks.inspectit.ocelot.core.instrumentation.context.ContextManager; import rocks.inspectit.ocelot.core.instrumentation.context.InspectitContextImpl; import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; +import rocks.inspectit.ocelot.core.selfmonitoring.ActionScopeFactory; import java.util.Arrays; @@ -30,6 +31,9 @@ public class MethodHookTest { @Mock private MethodReflectionInformation methodInfo; + @Mock + private ActionScopeFactory actionScopeFactory; + @BeforeEach void setupContextManagerMock() { when(contextManager.enterNewContext()).thenReturn(context); @@ -49,6 +53,7 @@ void testExceptionHandling() { .methodInformation(methodInfo) .entryActions(Arrays.asList(first, second, third)) .methodInformation(Mockito.mock(MethodReflectionInformation.class)) + .actionScopeFactory(actionScopeFactory) .build(); InternalInspectitContext ctx = hook.onEnter(null, null); @@ -81,6 +86,7 @@ void testReactivationOfActionsOnCopy() { .methodInformation(methodInfo) .entryAction(action) .methodInformation(Mockito.mock(MethodReflectionInformation.class)) + .actionScopeFactory(actionScopeFactory) .build(); InternalInspectitContext ctx = hook.onEnter(null, null); @@ -117,6 +123,7 @@ void testExceptionHandling() { .methodInformation(methodInfo) .exitActions(Arrays.asList(first, second, third)) .methodInformation(Mockito.mock(MethodReflectionInformation.class)) + .actionScopeFactory(actionScopeFactory) .build(); InternalInspectitContext ctx = hook.onEnter(null, null); @@ -143,6 +150,7 @@ void testReactivationOfActionsOnCopy() { .methodInformation(methodInfo) .exitAction(action) .methodInformation(Mockito.mock(MethodReflectionInformation.class)) + .actionScopeFactory(actionScopeFactory) .build(); InternalInspectitContext ctx = hook.onEnter(null, null); From 309f6211c7090cd8744944f05fd957ad5abf7bf8 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 25 Nov 2021 14:19:16 +0100 Subject: [PATCH 07/86] Refactored BoundGenericAction#getName to only return the action's name, and added BoundGenericAction#toString that returns the action's name and the name of the call. --- .../instrumentation/actions/bound/BoundGenericAction.java | 5 +++++ .../ocelot/core/instrumentation/hook/MethodHook.java | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/actions/bound/BoundGenericAction.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/actions/bound/BoundGenericAction.java index 1b0769e765..2b7204262e 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/actions/bound/BoundGenericAction.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/actions/bound/BoundGenericAction.java @@ -57,6 +57,11 @@ protected BoundGenericAction(String callName, GenericActionConfig actionConfig, @Override public String getName() { + return actionName; + } + + @Override + public String toString() { return "Action '" + actionName + "' for call '" + callName + "'"; } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java index 4b83e8c99f..4dd0be1cc7 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java @@ -97,7 +97,7 @@ public InternalInspectitContext onEnter(Object[] args, Object thiz) { try (IActionScope scope = actionScopeFactory.getScope(action)) { action.execute(executionContext); } catch (Throwable t) { - log.error("Entry action {} executed for method {} threw an exception and from now on is disabled!", action.getName(), methodInformation.getMethodFQN(), t); + log.error("Entry action {} executed for method {} threw an exception and from now on is disabled!", action, methodInformation.getMethodFQN(), t); activeEntryActions.remove(action); } } @@ -113,7 +113,7 @@ public void onExit(Object[] args, Object thiz, Object returnValue, Throwable thr try (IActionScope scope = actionScopeFactory.getScope(action)) { action.execute(executionContext); } catch (Throwable t) { - log.error("Exit action {} executed for method {} threw an exception and from now on is disabled!", action.getName(), methodInformation.getMethodFQN(), t); + log.error("Exit action {} executed for method {} threw an exception and from now on is disabled!", action, methodInformation.getMethodFQN(), t); activeExitActions.remove(action); } } From 50d25a82b21f2204196a4399652b1a23fa833312 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 29 Nov 2021 16:17:17 +0100 Subject: [PATCH 08/86] Upgraded OpenCensus to v0.28.3 --- gradle.properties | 6 ++++-- inspectit-ocelot-core/build.gradle | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index d3b8fc148d..a4a79e69c9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,7 @@ org.gradle.jvmargs=-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/circleci/inspectit/repo/heapdump.bin # Ensure to adapt the netty version (inspectit-ocelot-core/build.gradle) when changing the OpenCensus version -openCensusVersion=0.22.1.ocelot.1 +openCensusVersion=0.28.3 springVersion=2.1.1.RELEASE -prometheusClientVersion=0.6.0 \ No newline at end of file +prometheusClientVersion=0.6.0 +# appropriate netty version, see https://github.com/census-instrumentation/opencensus-java/blob/master/exporters/trace/ocagent/README.md +tcnativeVersion=2.0.20.Final \ No newline at end of file diff --git a/inspectit-ocelot-core/build.gradle b/inspectit-ocelot-core/build.gradle index 2d9526facb..b2e1d096a0 100644 --- a/inspectit-ocelot-core/build.gradle +++ b/inspectit-ocelot-core/build.gradle @@ -74,7 +74,7 @@ dependencies { "io.opencensus:opencensus-exporter-trace-ocagent:${openCensusVersion}", // The following dependecy is required for the OC-exporter to work correctly and must be matched against the grpc version // See https://github.com/census-instrumentation/opencensus-java/blob/master/exporters/trace/ocagent/README.md - 'io.netty:netty-tcnative-boringssl-static:2.0.20.Final', + "io.netty:netty-tcnative-boringssl-static:${tcnativeVersion}", "rocks.inspectit:opencensus-influxdb-exporter:1.2", From 6f74fe0a6860137744a56f817a65a9d998dae7d3 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 30 Nov 2021 15:25:21 +0100 Subject: [PATCH 09/86] Changed gson version to v2.8.5 to work with checkDependencyJavaVersions --- inspectit-ocelot-core/build.gradle | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/inspectit-ocelot-core/build.gradle b/inspectit-ocelot-core/build.gradle index b2e1d096a0..012f48596f 100644 --- a/inspectit-ocelot-core/build.gradle +++ b/inspectit-ocelot-core/build.gradle @@ -72,15 +72,17 @@ dependencies { "io.opencensus:opencensus-exporter-metrics-ocagent:${openCensusVersion}", "io.opencensus:opencensus-exporter-trace-ocagent:${openCensusVersion}", - // The following dependecy is required for the OC-exporter to work correctly and must be matched against the grpc version + // The following dependency is required for the OC-exporter to work correctly and must be matched against the grpc version // See https://github.com/census-instrumentation/opencensus-java/blob/master/exporters/trace/ocagent/README.md "io.netty:netty-tcnative-boringssl-static:${tcnativeVersion}", - "rocks.inspectit:opencensus-influxdb-exporter:1.2", - + // bytecode manipulation - "net.bytebuddy:byte-buddy:1.11.15" + "net.bytebuddy:byte-buddy:1.11.15", + + // use older gson version than v2.8.6 (what OC v0.26+ uses), as v2.8.6 does not work with checkDependencyJavaVersions, see https://github.com/google/gson/issues/1627 + "com.google.code.gson:gson:2.8.5!!" ) testImplementation( From 4546f2212817fe16098d344922c1a53f6fbe7997 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 10 Dec 2021 11:07:57 +0100 Subject: [PATCH 10/86] Started the integration of the OpenCensus-OpenTelemetry-Bridge (i.e., opencensus-shim), see #1246. Implemented a LoggingExporterService that exports traces to the LoggingSpanExporter and metrics to the LoggingMetricExporter. --- .../build.gradle | 3 +- .../file/accessor/git/RevisionAccess.java | 9 +- gradle.properties | 2 + inspectit-ocelot-agent/build.gradle | 67 ++-- .../inspectit/ocelot/agent/AgentMain.java | 111 +++++- .../inspectit/ocelot/utils/TestUtils.java | 86 +++-- .../MetricLoggingExporterSettings.java | 26 ++ .../metrics/MetricsExportersSettings.java | 3 + .../trace/TraceExportersSettings.java | 3 + .../trace/TraceLoggingExporterSettings.java | 17 + .../model/tracing/PropagationFormat.java | 12 +- .../ocelot/config/default/exporters.yml | 12 + inspectit-ocelot-core/build.gradle | 58 ++- .../inspectit/ocelot/core/AgentImpl.java | 8 +- .../DynamicallyActivatableExporter.java | 13 + .../DynamicallyActivatableMetricExporter.java | 79 +++++ .../DynamicallyActivatableSampler.java | 72 ++++ .../DynamicallyActivatableSpanExporter.java | 85 +++++ .../DynamicallyActivatableSpanProcessor.java | 63 ++++ .../core/exporter/JaegerExporterService.java | 8 +- .../core/exporter/LoggingExporterService.java | 225 ++++++++++++ ...OpenCensusAgentMetricsExporterService.java | 5 +- .../OpenCensusAgentTraceExporterService.java | 8 +- .../exporter/PrometheusExporterService.java | 6 +- .../core/exporter/ZipkinExporterService.java | 9 +- .../context/ContextPropagationUtil.java | 27 +- .../core/utils/OpenCensusShimUtils.java | 74 ++++ .../ocelot/core/utils/ReflectionUtils.java | 39 +++ .../exporter/LoggingExporterServiceTest.java | 329 ++++++++++++++++++ ...susAgentMetricsExporterServiceIntTest.java | 129 ------- ...ensusAgentTraceExporterServiceIntTest.java | 207 ----------- ...tryAgentMetricsExporterServiceIntTest.java | 6 + ...metryAgentTraceExporterServiceIntTest.java | 6 + .../ZipkinExporterServiceIntTest.java | 15 +- .../core/util/OpenCensusShimUtilsTest.java | 47 +++ 35 files changed, 1397 insertions(+), 472 deletions(-) create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricLoggingExporterSettings.java create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceLoggingExporterSettings.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableExporter.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricExporter.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterServiceTest.java delete mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterServiceIntTest.java delete mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterServiceIntTest.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentMetricsExporterServiceIntTest.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentTraceExporterServiceIntTest.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/OpenCensusShimUtilsTest.java diff --git a/components/inspectit-ocelot-configurationserver/build.gradle b/components/inspectit-ocelot-configurationserver/build.gradle index 4198a134d8..1fa11d3fef 100644 --- a/components/inspectit-ocelot-configurationserver/build.gradle +++ b/components/inspectit-ocelot-configurationserver/build.gradle @@ -140,7 +140,8 @@ dependencies { "org.eclipse.jgit:org.eclipse.jgit:5.7.0.202003110725-r", "com.google.code.gson:gson:2.8.5", - "io.opencensus:opencensus-impl:${openCensusVersion}" + "io.opentelemetry:opentelemetry-sdk:${openTelemetryVersion}", + "io.opentelemetry:opentelemetry-opencensus-shim:${openTelemetryAlphaVersion}" ) testImplementation( 'org.springframework.boot:spring-boot-starter-test:2.1.6.RELEASE', diff --git a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/git/RevisionAccess.java b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/git/RevisionAccess.java index 51b10fc06c..c2c6f7564d 100644 --- a/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/git/RevisionAccess.java +++ b/components/inspectit-ocelot-configurationserver/src/main/java/rocks/inspectit/ocelot/file/accessor/git/RevisionAccess.java @@ -18,7 +18,10 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; /** * Accessor to access specific Git revision/commits. Using this class ensures that all operations will be executed @@ -110,7 +113,7 @@ public RevisionAccess getCommonAncestor(RevisionAccess other) { RevWalk walk = new RevWalk(repository); walk.setRevFilter(RevFilter.MERGE_BASE); // RevCommits need to be produced by the same RevWalk instance otherwise it can't compare them - walk.markStart(walk.parseCommit(this.revCommit.toObjectId())); + walk.markStart(walk.parseCommit(revCommit.toObjectId())); walk.markStart(walk.parseCommit(other.revCommit.toObjectId())); RevCommit mergeBase = walk.next(); @@ -196,7 +199,7 @@ protected String verifyPath(String relativeBasePath, String relativePath) throws throw new IllegalArgumentException("User path escapes the base path: " + relativePath); } - return resolvedPath.toString().replace("\\\\", "/"); + return resolvedPath.toString().replaceAll("\\\\", "/"); } @Override diff --git a/gradle.properties b/gradle.properties index a4a79e69c9..b27171e1cd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,8 @@ org.gradle.jvmargs=-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/circleci/inspectit/repo/heapdump.bin # Ensure to adapt the netty version (inspectit-ocelot-core/build.gradle) when changing the OpenCensus version openCensusVersion=0.28.3 +openTelemetryVersion=1.9.1 +openTelemetryAlphaVersion=1.9.1-alpha springVersion=2.1.1.RELEASE prometheusClientVersion=0.6.0 # appropriate netty version, see https://github.com/census-instrumentation/opencensus-java/blob/master/exporters/trace/ocagent/README.md diff --git a/inspectit-ocelot-agent/build.gradle b/inspectit-ocelot-agent/build.gradle index 928930cfb4..9419089dd5 100644 --- a/inspectit-ocelot-agent/build.gradle +++ b/inspectit-ocelot-agent/build.gradle @@ -20,7 +20,7 @@ group = 'rocks.inspectit.ocelot' sourceCompatibility = 1.8 configurations { - opencensus + opentelemetry } dependencies { @@ -28,11 +28,14 @@ dependencies { project(':inspectit-ocelot-bootstrap'), 'org.projectlombok:lombok:1.18.12' ) - opencensus( - "io.opencensus:opencensus-impl:${openCensusVersion}" + + opentelemetry( + "io.opentelemetry:opentelemetry-sdk:${openTelemetryVersion}", + "io.opentelemetry:opentelemetry-opencensus-shim:${openTelemetryAlphaVersion}" ) + annotationProcessor 'org.projectlombok:lombok:1.18.12' - testImplementation ( + testImplementation( 'org.junit.jupiter:junit-jupiter-api:5.3.1', 'org.mockito:mockito-junit-jupiter:2.23.0', 'ch.qos.logback:logback-classic:1.1.3', @@ -40,11 +43,12 @@ dependencies { testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1' } -task buildOpencensusFatJar(type: Jar) { - archiveFileName = "opencensus-fat.jar" + +task buildOpenTelemetryFatJar(type: Jar){ + archiveFileName = "opentelemetry-fat.jar" destinationDirectory = file("$buildDir/jarGen") - from { - configurations.opencensus.collect { it.isDirectory() ? it : zipTree(it) } + from{ + configurations.opentelemetry.collect{it.isDirectory() ? it : zipTree(it) } } } @@ -57,8 +61,8 @@ jar { from file("src/main/resources/META-INF/MANIFEST.MF") } - //include the open-census dependencies as a fat jar - dependsOn buildOpencensusFatJar + // include the open-telemetry dependencies as a fat jar + dependsOn buildOpenTelemetryFatJar from "$buildDir/jarGen" from project(':inspectit-ocelot-bootstrap').jar.outputs @@ -91,17 +95,18 @@ configurations { dependencies { systemTestCompileOnly( - "io.opencensus:opencensus-impl:${openCensusVersion}", + "io.opentelemetry:opentelemetry-sdk:${openTelemetryVersion}", + "io.opentelemetry:opentelemetry-opencensus-shim:${openTelemetryAlphaVersion}", project(':inspectit-ocelot-bootstrap') ) systemTestImplementation( "io.opencensus:opencensus-testing:${openCensusVersion}", "io.opencensus:opencensus-api:${openCensusVersion}", - + 'org.junit.jupiter:junit-jupiter-engine:5.3.1', 'org.junit.jupiter:junit-jupiter-api:5.3.1', - + 'org.assertj:assertj-core:3.11.1', 'org.awaitility:awaitility:3.1.5', "com.github.tomakehurst:wiremock:2.25.1", @@ -109,7 +114,7 @@ dependencies { 'org.apache.httpcomponents:httpclient:4.5.6', 'org.eclipse.jetty:jetty-server:9.4.3.v20170317', 'org.eclipse.jetty:jetty-servlet:9.4.3.v20170317', - + 'com.h2database:h2:1.4.199', // for log-correlation tests @@ -120,8 +125,8 @@ dependencies { ) jmh( - sourceSets.systemTest.output, - project(':inspectit-ocelot-bootstrap') + sourceSets.systemTest.output, + project(':inspectit-ocelot-bootstrap') ) } @@ -139,22 +144,24 @@ task systemTest { def javaExecutables = ("${systemTestJavaHomes}") .tokenize(File.pathSeparator) - .collect { org.apache.tools.ant.taskdefs.condition.Os.isFamily( - org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS) - ? "${it}/bin/java.exe" - : "${it}/bin/java" } - .collect { new File(it).getCanonicalPath() } + .collect { + org.apache.tools.ant.taskdefs.condition.Os.isFamily( + org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS) + ? "${it}/bin/java.exe" + : "${it}/bin/java" + } + .collect { new File(it).getCanonicalPath() } .unique() - assert javaExecutables.size > 0 : + assert javaExecutables.size > 0: 'No Java executables found for running system tests' javaExecutables.eachWithIndex { javaExecutable, index -> def paths = "${javaExecutable}".tokenize(File.separator) def javaName = "${index}" - if(paths.size > 3) { - javaName = paths[paths.size - 4] + "_" + paths[paths.size - 3] - } else if(paths.length > 2) { + if (paths.size > 3) { + javaName = paths[paths.size - 4] + "_" + paths[paths.size - 3] + } else if (paths.length > 2) { javaName = paths[paths.size - 3] } @@ -200,14 +207,16 @@ task systemTest { standardOutput = os errorOutput = os } - if(!os.toString().contains("\"1.8.")) { + if (!os.toString().contains("\"1.8.")) { //Turns the jigsaw warnings into errors, making the system tests fail in case of illegal accesses jvmArgs "--illegal-access=deny" } - jvmArgs "-Dinspectit.config.file-based.path=$projectDir/src/system-test/resources/config" // make inspectIT scan the workdir for configs - jvmArgs "-Dinspectit.publishOpenCensusToBootstrap=true" // make inspectIT push OC to the bootstrap - jvmArgs jacoco.asJvmArg + ",inclbootstrapclasses=true" // JaCoCo agent first + bootstrap instrumentation + jvmArgs "-Dinspectit.config.file-based.path=$projectDir/src/system-test/resources/config" + // make inspectIT scan the workdir for configs + jvmArgs "-Dinspectit.publishOpenTelemetryToBootstrap=true" // make inspectIT push OTel to the bootstrap + jvmArgs jacoco.asJvmArg + ",inclbootstrapclasses=true" + // JaCoCo agent first + bootstrap instrumentation jvmArgs "-javaagent:${agentJarPath}" // Our agent second. jacoco.enabled = false // Don't add the JaCoCo agent again. diff --git a/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java b/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java index 8b97e66c65..50f13a2d28 100644 --- a/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java +++ b/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java @@ -1,5 +1,7 @@ package rocks.inspectit.ocelot.agent; +import lombok.AllArgsConstructor; +import lombok.Value; import rocks.inspectit.ocelot.bootstrap.AgentManager; import rocks.inspectit.ocelot.bootstrap.Instances; @@ -15,6 +17,7 @@ import java.security.CodeSource; import java.security.PermissionCollection; import java.util.jar.JarFile; +import java.util.logging.Logger; /** * Entry point of the agent. @@ -24,10 +27,17 @@ public class AgentMain { private static final String INSPECTIT_BOOTSTRAP_JAR_PATH = "/inspectit-ocelot-bootstrap.jar"; + private static final String INSPECTIT_CORE_JAR_PATH = "/inspectit-ocelot-core.jar"; - private static final String OPENCENSUS_FAT_JAR_PATH = "/opencensus-fat.jar"; + private static final String PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY = "inspectit.publishOpenCensusToBootstrap"; + private static final String OPEN_TELEMETRY_FAT_JAR_PATH = "/opentelemetry-fat.jar"; + + private static final String PUBLISH_OPEN_TELEMETRY_TO_BOOTSTRAP_PROPERTY = "inspectit.publishOpenTelemetryToBootstrap"; + + private static Logger LOGGER = Logger.getLogger(AgentMain.class.getName()); + /** * Main method for attaching the agent itself to a running JVM. * @@ -51,16 +61,22 @@ public static void agentmain(String agentArgs, Instrumentation inst) { } public static void premain(String agentArgs, Instrumentation inst) { - boolean loadOpenCensusToBootstrap = "true".equalsIgnoreCase(System.getProperty(PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY)); + boolean loadOpenTelemetryJarToBootstrap = null != System.getProperty(PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY) ? "true".equalsIgnoreCase(PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY) : "true".equalsIgnoreCase(System.getProperty(PUBLISH_OPEN_TELEMETRY_TO_BOOTSTRAP_PROPERTY)); + // check for deprecated JVM property + if (null != System.getProperty(PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY)) { + LOGGER.warning("You are using the deprecated JVM property '" + PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY + "'. Please use the new JVM property '" + PUBLISH_OPEN_TELEMETRY_TO_BOOTSTRAP_PROPERTY + "'. inspectIT Ocelot has moved from OpenCensus to OpenTelemetry. However, applications using the OpenCensusAPI are still supported through the opentelemetry-opencensus-shim "); + } + // retrieve and build settings for the telemetry implementation + TelemetrySettings telemetrySettings = new TelemetrySettings(TelemetryImplementation.OPEN_TELEMETRY, loadOpenTelemetryJarToBootstrap, PUBLISH_OPEN_TELEMETRY_TO_BOOTSTRAP_PROPERTY); + try { - if (loadOpenCensusToBootstrap) { - Path ocJarFile = copyResourceToTempJarFile(OPENCENSUS_FAT_JAR_PATH); - inst.appendToBootstrapClassLoaderSearch(new JarFile(ocJarFile.toFile())); + if (loadOpenTelemetryJarToBootstrap) { + Path telJarFile = copyResourceToTempJarFile(OPEN_TELEMETRY_FAT_JAR_PATH); + inst.appendToBootstrapClassLoaderSearch(new JarFile(telJarFile.toFile())); } + //we make sure that the startup of inspectIT is asynchronous - new Thread(() -> - startAgent(agentArgs, inst, !loadOpenCensusToBootstrap) - ).start(); + new Thread(() -> startAgent(agentArgs, inst, !loadOpenTelemetryJarToBootstrap)).start(); } catch (Exception e) { System.err.println("Error starting inspectIT Agent!"); e.printStackTrace(); @@ -77,13 +93,24 @@ private static void startAgent(String agentArgs, Instrumentation inst, boolean i } } + private static void startAgent(String agentArgs, Instrumentation inst, TelemetrySettings telemetrySettings) { + try { + InspectITClassLoader icl = initializeInspectitLoader(inst, telemetrySettings); + AgentManager.startOrReplaceInspectitCore(icl, agentArgs, inst); + } catch (Exception e) { + System.err.println("Error starting inspectIT Agent!"); + e.printStackTrace(); + } + } + /** * Loads {@link #INSPECTIT_BOOTSTRAP_JAR_PATH} with the bootstrap classloader and @link {@link #INSPECTIT_CORE_JAR_PATH} with a new inspectIT loader. * * @return the created inspectIT classloader + * * @throws IOException */ - private static InspectITClassLoader initializeInspectitLoader(Instrumentation inst, boolean includeOpenCensus) throws IOException { + private static InspectITClassLoader initializeInspectitLoader(Instrumentation inst, boolean includeOpenTelemetry) throws IOException { Path bootstrapJar = copyResourceToTempJarFile(INSPECTIT_BOOTSTRAP_JAR_PATH); inst.appendToBootstrapClassLoaderSearch(new JarFile(bootstrapJar.toFile())); @@ -94,9 +121,36 @@ private static InspectITClassLoader initializeInspectitLoader(Instrumentation in Path coreJar = copyResourceToTempJarFile(INSPECTIT_CORE_JAR_PATH); InspectITClassLoader icl = new InspectITClassLoader(new URL[]{coreJar.toUri().toURL()}); - if (includeOpenCensus) { - Path ocJarFile = copyResourceToTempJarFile(OPENCENSUS_FAT_JAR_PATH); - icl.addURL(ocJarFile.toUri().toURL()); + if (includeOpenTelemetry) { + Path otJarFile = copyResourceToTempJarFile(OPEN_TELEMETRY_FAT_JAR_PATH); + icl.addURL(otJarFile.toUri().toURL()); + } + + return icl; + } + + /** + * Loads {@link #INSPECTIT_BOOTSTRAP_JAR_PATH} with the bootstrap classloader and @link {@link #INSPECTIT_CORE_JAR_PATH} with a new inspectIT loader. + * + * @return the created inspectIT classloader + * + * @throws IOException + */ + private static InspectITClassLoader initializeInspectitLoader(Instrumentation inst, TelemetrySettings telemetrySettings) throws IOException { + Path bootstrapJar = copyResourceToTempJarFile(INSPECTIT_BOOTSTRAP_JAR_PATH); + inst.appendToBootstrapClassLoaderSearch(new JarFile(bootstrapJar.toFile())); + + Instances.BOOTSTRAP_JAR_URL = bootstrapJar.toUri().toURL(); + + Instances.AGENT_JAR_URL = AgentMain.class.getProtectionDomain().getCodeSource().getLocation(); + + Path coreJar = copyResourceToTempJarFile(INSPECTIT_CORE_JAR_PATH); + InspectITClassLoader icl = new InspectITClassLoader(new URL[]{coreJar.toUri().toURL()}); + + // add the fat jar of the telemetry if it has not been published to bootstrap already + if (!telemetrySettings.isPublishToBootstrap()) { + Path telJarFile = copyResourceToTempJarFile(telemetrySettings.getPathToFatJar()); + icl.addURL(telJarFile.toUri().toURL()); } return icl; @@ -106,7 +160,9 @@ private static InspectITClassLoader initializeInspectitLoader(Instrumentation in * Copies the given resource to a new temporary file with the ending ".jar" * * @param resourcePath the path to the resource + * * @return the path to the generated jar file + * * @throws IOException */ private static Path copyResourceToTempJarFile(String resourcePath) throws IOException { @@ -157,7 +213,8 @@ private static ClassLoader findParentClassLoader() { if (javaVersion.startsWith("1.8")) { return null; } else { - return (ClassLoader) ClassLoader.class.getDeclaredMethod("getPlatformClassLoader", new Class[]{}).invoke(null); + return (ClassLoader) ClassLoader.class.getDeclaredMethod("getPlatformClassLoader", new Class[]{}) + .invoke(null); } } catch (Exception e) { return null; @@ -165,4 +222,32 @@ private static ClassLoader findParentClassLoader() { } } +} + +/** + * The telemetry implementation for collecting metrics and traces + */ +enum TelemetryImplementation { + UNKNOWN, OPEN_CENSUS, OPEN_TELEMETRY, COUNT +} + +@Value +@AllArgsConstructor +class TelemetrySettings { + + /** + * The {@link TelemetryImplementation} + */ + TelemetryImplementation telemetryImplementation; + + /** + * Whether the jar of the {@link TelemetryImplementation} should be published/loaded in the bootstrap classloader + */ + boolean publishToBootstrap; + + /** + * The path to the fat.jar of the {@link TelemetryImplementation} + */ + String pathToFatJar; + } \ No newline at end of file diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java index 735a472ae6..18fa4545a4 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java @@ -1,7 +1,6 @@ package rocks.inspectit.ocelot.utils; import com.google.common.cache.Cache; -import io.opencensus.impl.internal.DisruptorEventQueue; import io.opencensus.metrics.LabelKey; import io.opencensus.metrics.LabelValue; import io.opencensus.metrics.Metrics; @@ -92,8 +91,7 @@ private static synchronized Cache, Object> getInstrumentationCache() { getBean.setAccessible(true); Object instrumentationManager = getBean.invoke(ctx, "instrumentationManager"); - activeInstrumentations = (Cache, Object>) getField(instrumentationManager.getClass(), "activeInstrumentations") - .get(instrumentationManager); + activeInstrumentations = (Cache, Object>) getField(instrumentationManager.getClass(), "activeInstrumentations").get(instrumentationManager); } catch (Exception ex) { throw new RuntimeException(ex); } @@ -185,9 +183,10 @@ public static void waitForClassHooks(List> clazzes, int duration, TimeU */ public static void waitForOpenCensusQueueToBeProcessed() { CountDownLatch latch = new CountDownLatch(1); - DisruptorEventQueue.getInstance().enqueue(latch::countDown); + // TODO: re-implement with OTel + // DisruptorEventQueue.getInstance().enqueue(latch::countDown); try { - latch.await(30, TimeUnit.SECONDS); + latch.await(3, TimeUnit.SECONDS); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -226,6 +225,7 @@ public static Map getCurrentTagsAsMap() { * * @param viewName the name of the views * @param tags the expected tag values + * * @return the found aggregation data, null otherwise */ public static AggregationData getDataForView(String viewName, Map tags) { @@ -239,21 +239,17 @@ public static AggregationData getDataForView(String viewName, Map expectedTagValues = orderedTagKeys.stream().map(tags::get).collect(Collectors.toList()); - return view.getAggregationMap().entrySet() - .stream() - .filter(e -> { - List tagValues = e.getKey(); - for (int i = 0; i < tagValues.size(); i++) { - String regex = expectedTagValues.get(i); - TagValue tagValue = tagValues.get(i); - if (regex != null && (tagValue == null || !tagValue.asString().matches(regex))) { - return false; - } - } - return true; - }) - .map(Map.Entry::getValue) - .findFirst().orElse(null); + return view.getAggregationMap().entrySet().stream().filter(e -> { + List tagValues = e.getKey(); + for (int i = 0; i < tagValues.size(); i++) { + String regex = expectedTagValues.get(i); + TagValue tagValue = tagValues.get(i); + if (regex != null && (tagValue == null || !tagValue.asString().matches(regex))) { + return false; + } + } + return true; + }).map(Map.Entry::getValue).findFirst().orElse(null); } public static TimeSeries getTimeseries(String metricName, Map tags) { @@ -265,25 +261,25 @@ public static TimeSeries getTimeseries(String metricName, Map ta .filter(m -> m.getMetricDescriptor().getName().equals(metricName)) .flatMap(m -> { List orderedTagKeys = m.getMetricDescriptor() - .getLabelKeys().stream() + .getLabelKeys() + .stream() .map(LabelKey::getKey) .collect(Collectors.toList()); assertThat(orderedTagKeys).contains(tags.keySet().toArray(new String[]{})); List expectedTagValues = orderedTagKeys.stream() .map(tags::get) .collect(Collectors.toList()); - return m.getTimeSeriesList().stream() - .filter(ts -> { - List tagValues = ts.getLabelValues(); - for (int i = 0; i < tagValues.size(); i++) { - String regex = expectedTagValues.get(i); - LabelValue tagValue = tagValues.get(i); - if (regex != null && (tagValue == null || !tagValue.getValue().matches(regex))) { - return false; - } - } - return true; - }); + return m.getTimeSeriesList().stream().filter(ts -> { + List tagValues = ts.getLabelValues(); + for (int i = 0; i < tagValues.size(); i++) { + String regex = expectedTagValues.get(i); + LabelValue tagValue = tagValues.get(i); + if (regex != null && (tagValue == null || !tagValue.getValue().matches(regex))) { + return false; + } + } + return true; + }); }) .findFirst(); assertThat(series).isNotEmpty(); @@ -292,23 +288,23 @@ public static TimeSeries getTimeseries(String metricName, Map ta private static long getInstrumentationQueueLength() { ViewManager viewManager = Stats.getViewManager(); - AggregationData.LastValueDataLong queueSize = - (AggregationData.LastValueDataLong) - viewManager.getView(View.Name.create("inspectit/self/instrumentation-queue-size")) - .getAggregationMap().values().stream() - .findFirst() - .get(); + AggregationData.LastValueDataLong queueSize = (AggregationData.LastValueDataLong) viewManager.getView(View.Name.create("inspectit/self/instrumentation-queue-size")) + .getAggregationMap() + .values() + .stream() + .findFirst() + .get(); return queueSize.getLastValue(); } private static long getInstrumentationClassesCount() { ViewManager viewManager = Stats.getViewManager(); - AggregationData.LastValueDataLong queueSize = - (AggregationData.LastValueDataLong) - viewManager.getView(View.Name.create("inspectit/self/instrumented-classes")) - .getAggregationMap().values().stream() - .findFirst() - .get(); + AggregationData.LastValueDataLong queueSize = (AggregationData.LastValueDataLong) viewManager.getView(View.Name.create("inspectit/self/instrumented-classes")) + .getAggregationMap() + .values() + .stream() + .findFirst() + .get(); return queueSize.getLastValue(); } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricLoggingExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricLoggingExporterSettings.java new file mode 100644 index 0000000000..347b837934 --- /dev/null +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricLoggingExporterSettings.java @@ -0,0 +1,26 @@ +package rocks.inspectit.ocelot.config.model.exporters.metrics; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.time.DurationMin; + +import java.time.Duration; + +/** + * Settings for the {@link io.opentelemetry.exporter.logging.LoggingMetricExporter} and + */ +@Data +@NoArgsConstructor +public class MetricLoggingExporterSettings { + + private boolean enabled; + + private String serviceName; + + /** + * Defines how often metrics are pushed to the log. + */ + @DurationMin(millis = 1) + private Duration exportInterval; + +} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java index 4e4dcb1221..6d13169ad7 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java @@ -20,4 +20,7 @@ public class MetricsExportersSettings { @Valid private InfluxExporterSettings influx; + + @Valid + private MetricLoggingExporterSettings logging; } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java index 2c58311299..4b546f2a87 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java @@ -17,4 +17,7 @@ public class TraceExportersSettings { @Valid private OpenCensusAgentTraceExporterSettings openCensusAgent; + + @Valid + private TraceLoggingExporterSettings logging; } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceLoggingExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceLoggingExporterSettings.java new file mode 100644 index 0000000000..9382f9aa16 --- /dev/null +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceLoggingExporterSettings.java @@ -0,0 +1,17 @@ +package rocks.inspectit.ocelot.config.model.exporters.trace; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Settings for the {@link io.opentelemetry.exporter.logging.LoggingMetricExporter} + */ +@Data +@NoArgsConstructor +public class TraceLoggingExporterSettings { + + private boolean enabled; + + private String serviceName; + +} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/PropagationFormat.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/PropagationFormat.java index eb6d390ae7..6cd1441f99 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/PropagationFormat.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/PropagationFormat.java @@ -18,6 +18,16 @@ public enum PropagationFormat { /** * Using Datadog headers. */ - DATADOG; + DATADOG, + + /** + * Using BaggageContext headers, seehttps://www.w3.org/TR/baggage/ + */ + W3C_BAGGAGE, + + /** + * Using Jaeger HTTP format, see https://www.jaegertracing.io/docs/1.29/client-libraries/#propagation-format + */ + JAEGER; } diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml index 8cc4e8dce4..548026b588 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml @@ -49,6 +49,13 @@ inspectit: # E.g. if the exportInterval is 15s and the buffer-size is 4, the export will keep up to one minute of data in memory. buffer-size: 40 + # settings for the LoggingMetricExporter used in LoggingExporterService + logging: + enabled: false + serviceName: ${inspectit.service-name} + # the export interval of the metrics + export-interval: ${inspectit.metrics.frequency} + # settings for trace exporters tracing: @@ -82,3 +89,8 @@ inspectit: service-name: ${inspectit.service-name} # the time at which the exporter tries to reconnect to the OpenCensus agent reconnection-period: 5s + + # settings for the SpanLoggingExporterService + logging: + enabled: true + service-name: ${inspectit.service-name} diff --git a/inspectit-ocelot-core/build.gradle b/inspectit-ocelot-core/build.gradle index 012f48596f..26aaaf3a65 100644 --- a/inspectit-ocelot-core/build.gradle +++ b/inspectit-ocelot-core/build.gradle @@ -23,9 +23,20 @@ sourceCompatibility = 1.8 dependencies { compileOnly( project(':inspectit-ocelot-bootstrap'), - "io.opencensus:opencensus-api:${openCensusVersion}", - "io.opencensus:opencensus-impl:${openCensusVersion}", - 'org.projectlombok:lombok:1.18.12' + 'org.projectlombok:lombok:1.18.12', + + // OpenTelemetry + platform("io.opentelemetry:opentelemetry-bom:${openTelemetryVersion}"), + platform("io.opentelemetry:opentelemetry-bom-alpha:${openTelemetryAlphaVersion}"), + 'io.opentelemetry:opentelemetry-api', + 'io.opentelemetry:opentelemetry-api-metrics', + "io.opentelemetry:opentelemetry-sdk", + "io.opentelemetry:opentelemetry-sdk-metrics", + "io.opentelemetry:opentelemetry-semconv", + // OC-OTel bridge + "io.opentelemetry:opentelemetry-opencensus-shim", + + ) buildTools( 'jarcheck:jarcheck:1.5' @@ -41,8 +52,8 @@ dependencies { project(':inspectit-ocelot-config'), project(':inspectit-ocelot-sdk'), - - // spring releated + + // spring related "org.springframework.boot:spring-boot:${springVersion}", 'org.yaml:snakeyaml:1.23', 'javax.annotation:javax.annotation-api:1.3.2', //Required for @PostConstruct and @PreDestroy to work in Java9+ @@ -65,13 +76,15 @@ dependencies { 'org.javassist:javassist:3.24.1-GA', // OpenCensus exporters - "io.opencensus:opencensus-exporter-stats-prometheus:${openCensusVersion}", "io.prometheus:simpleclient_httpserver:${prometheusClientVersion}", - "io.opencensus:opencensus-exporter-trace-zipkin:${openCensusVersion}", - "io.opencensus:opencensus-exporter-trace-jaeger:${openCensusVersion}", - "io.opencensus:opencensus-exporter-metrics-ocagent:${openCensusVersion}", - "io.opencensus:opencensus-exporter-trace-ocagent:${openCensusVersion}", + // we still need the OpenCensus SDK for the metric exporters to work, as the shim only includes opencensus-impl-core + "io.opencensus:opencensus-impl:${openCensusVersion}", + + // OpenTelemetry exporters + platform("io.opentelemetry:opentelemetry-bom:${openTelemetryVersion}"), + "io.opentelemetry:opentelemetry-exporter-logging", + // The following dependency is required for the OC-exporter to work correctly and must be matched against the grpc version // See https://github.com/census-instrumentation/opencensus-java/blob/master/exporters/trace/ocagent/README.md "io.netty:netty-tcnative-boringssl-static:${tcnativeVersion}", @@ -81,14 +94,32 @@ dependencies { // bytecode manipulation "net.bytebuddy:byte-buddy:1.11.15", + /* // use older gson version than v2.8.6 (what OC v0.26+ uses), as v2.8.6 does not work with checkDependencyJavaVersions, see https://github.com/google/gson/issues/1627 - "com.google.code.gson:gson:2.8.5!!" + "com.google.code.gson:gson:2.8.5!!", + + // use older version of jackson than 2.13.0 (what OTel uses), as v2.13.10 does not work with checkDependencyJavaVersions + "com.fasterxml.jackson.core:jackson-annotations:2.9.10!!", + "com.fasterxml.jackson.core:jackson-core:2.9.10!!", + "com.fasterxml.jackson.core:jackson-databind:2.9.10!!", + + // use opentelemetry-sdk-common without Java9+ code to avoid checkDependencyJavaVersions failures (this package is used by, for example, opentelemetry-exporter-logger) + "io.opentelemetry:opentelemetry-sdk-common:0.11.0!!", + */ ) testImplementation( project(':inspectit-ocelot-bootstrap'), - "io.opencensus:opencensus-impl:${openCensusVersion}", + platform("io.opentelemetry:opentelemetry-bom:${openTelemetryVersion}"), + platform("io.opentelemetry:opentelemetry-bom-alpha:${openTelemetryAlphaVersion}"), + "io.opentelemetry:opentelemetry-sdk", + "io.opentelemetry:opentelemetry-opencensus-shim", + "io.opentelemetry:opentelemetry-semconv", 'org.springframework:spring-test:5.1.3.RELEASE', + // to make use of SpyBean + "org.springframework.boot:spring-boot-test:2.1.4.RELEASE", + // LogCapturer + "io.github.netmikey.logunit:logunit-jul:1.1.0", 'org.apache.httpcomponents:httpclient:4.5.6', 'org.mockito:mockito-core:2.23.4', 'org.assertj:assertj-core:3.11.1', @@ -145,7 +176,8 @@ task generateVersionFile { } } -jar.dependsOn checkDependencyJavaVersions +// TODO: re-enable once we have excluded OTel +// jar.dependsOn checkDependencyJavaVersions jar.dependsOn generateVersionFile jar { diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/AgentImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/AgentImpl.java index 3aac4a8538..94c0aa79bf 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/AgentImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/AgentImpl.java @@ -66,7 +66,7 @@ public void start(String cmdArgs, Instrumentation instrumentation) { LOGGER.info("Starting inspectIT Ocelot Agent..."); LOGGER.info("\tVersion: {}", getVersion()); LOGGER.info("\tBuild Date: {}", getBuildDate()); - logOpenCensusClassLoader(); + logOpenTelemetryClassLoader(); ctx = new AnnotationConfigApplicationContext(); ctx.setClassLoader(classloader); @@ -84,11 +84,11 @@ public void start(String cmdArgs, Instrumentation instrumentation) { ctx.refresh(); } - private void logOpenCensusClassLoader() { + private void logOpenTelemetryClassLoader() { if (Tags.class.getClassLoader() == AgentImpl.class.getClassLoader()) { - LOGGER.info("OpenCensus was loaded in inspectIT classloader"); + LOGGER.info("OpenTelemetry was loaded in inspectIT classloader"); } else { - LOGGER.info("OpenCensus was loaded in bootstrap classloader"); + LOGGER.info("OpenTelemetry was loaded in bootstrap classloader"); } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableExporter.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableExporter.java new file mode 100644 index 0000000000..27a250aa8d --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableExporter.java @@ -0,0 +1,13 @@ +package rocks.inspectit.ocelot.core.exporter; + +import rocks.inspectit.ocelot.config.model.InspectitConfig; + +public interface DynamicallyActivatableExporter { + + boolean doDisable(); + + boolean doEnable(); + + boolean configurationChanged(InspectitConfig config); + +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricExporter.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricExporter.java new file mode 100644 index 0000000000..d27932d469 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricExporter.java @@ -0,0 +1,79 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import lombok.*; +import lombok.extern.slf4j.Slf4j; + +import java.util.Collection; + +/** + * A custom {@link MetricExporter} wrapper that can be dynamically enabled or disabled. + */ +@Slf4j +@Data +public class DynamicallyActivatableMetricExporter implements MetricExporter { + + /** + * The real {@link MetricExporter} implementation + */ + private T exporter; + + @Setter(AccessLevel.PRIVATE) + private boolean enabled = false; + + public DynamicallyActivatableMetricExporter(T metricExporter) { + exporter = metricExporter; + } + + /** + * Creates a new {@link DynamicallyActivatableMetricExporter} with the given {@link MetricExporter} as the implemented exporter + * + * @param metricExporter + * + * @return {@link DynamicallyActivatableMetricExporter} with the given {@link MetricExporter} as the implemented exporter + */ + public static DynamicallyActivatableMetricExporter create(MetricExporter metricExporter) { + return new DynamicallyActivatableMetricExporter<>(metricExporter); + } + + /** + * Creates a new {@link DynamicallyActivatableMetricExporter} with a {@link LoggingMetricExporter} as the implemented {@link MetricExporter} + * + * @return {@link DynamicallyActivatableMetricExporter} with a {@link LoggingMetricExporter} as the implemented {@link MetricExporter} + */ + public static DynamicallyActivatableMetricExporter createLoggingExporter() { + return new DynamicallyActivatableMetricExporter<>(new LoggingMetricExporter()); + } + + @Override + public CompletableResultCode export(Collection metrics) { + // if enabled, call the real exporter's export method, otherwise do nothing + return isEnabled() ? exporter.export(metrics) : CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode flush() { + return exporter.flush(); + } + + @Override + public CompletableResultCode shutdown() { + return exporter.shutdown(); + } + + public boolean doEnable() { + setEnabled(true); + return true; + } + + public boolean doDisable() { + setEnabled(false); + return true; + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java new file mode 100644 index 0000000000..67a5ebb4ed --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java @@ -0,0 +1,72 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; +import lombok.Data; + +import java.util.List; + +/** + * A custom {@link Sampler} wrapper that can be dynamically enabled or disabled. + */ +@Data +public class DynamicallyActivatableSampler implements Sampler { + + private boolean enabled = true; + + /** + * The real implementation of the {@link Sampler} + */ + private Sampler sampler; + + + private DynamicallyActivatableSampler(Sampler sampler) { + this.sampler = sampler; + } + + /** + * Creates a new {@link DynamicallyActivatableSampler} for the given {@link Sampler} implementation + * @param sampler The underlying {@link Sampler} implementation + */ + public static DynamicallyActivatableSampler create(Sampler sampler){ + return new DynamicallyActivatableSampler(sampler); + } + + public static DynamicallyActivatableSampler createAlwaysOn() { + return new DynamicallyActivatableSampler(Sampler.alwaysOn()); + } + + public static DynamicallyActivatableSampler createAlwaysOff() { + return new DynamicallyActivatableSampler(Sampler.alwaysOff()); + } + + public static DynamicallyActivatableSampler createParentBased(Sampler root) { + return new DynamicallyActivatableSampler(Sampler.parentBased(root)); + } + + public static DynamicallyActivatableSampler createRatio(double ratio) { + return new DynamicallyActivatableSampler(Sampler.traceIdRatioBased(ratio)); + } + + @Override + public SamplingResult shouldSample(Context parentContext, String traceId, String name, SpanKind spanKind, Attributes attributes, List parentLinks) { + + // when enabled, call the 'real' sampler's shouldSample method + if (isEnabled()) { + return sampler.shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); + } + // otherwise, call the method of the alwaysOff() sampler + else { + return Sampler.alwaysOff().shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); + } + } + + @Override + public String getDescription() { + return String.format("Custom {}", sampler.getDescription()); + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java new file mode 100644 index 0000000000..dee4ca2c49 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java @@ -0,0 +1,85 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import lombok.AccessLevel; +import lombok.Data; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.util.Collection; + +/** + * A custom {@link SpanExporter} wrapper that can be dynamically enabled or disabled. + */ +@Data +@Slf4j +public class DynamicallyActivatableSpanExporter implements SpanExporter { + + @Setter(AccessLevel.PRIVATE) + private boolean enabled; + + /** + * The underlying implementation of the {@link SpanExporter} + */ + private T exporter; + + private DynamicallyActivatableSpanExporter(T spanExporter) { + exporter = spanExporter; + } + + /** + * Creates a new {@link DynamicallyActivatableSpanExporter} with the given {@link SpanExporter} as the implemented exporter + * + * @param spanExporter + * + * @return {@link DynamicallyActivatableSpanExporter} with the given {@link SpanExporter} as the implemented exporter + */ + public static DynamicallyActivatableSpanExporter create(SpanExporter spanExporter) { + return new DynamicallyActivatableSpanExporter(spanExporter); + } + + /** + * Creates a new {@link DynamicallyActivatableSpanExporter} with a {@link LoggingSpanExporter} as the implemented {@link SpanExporter} + * + * @return {@link DynamicallyActivatableSpanExporter} with a {@link LoggingSpanExporter} as the implemented {@link SpanExporter} + */ + public static DynamicallyActivatableSpanExporter createLoggingSpanExporter() { + return new DynamicallyActivatableSpanExporter<>(new LoggingSpanExporter()); + } + + @Override + public CompletableResultCode export(Collection spans) { + // if enabled, call the real exporter's export method + if (isEnabled()) { + return exporter.export(spans); + } + // otherwise, do nothing and return success + else { + return CompletableResultCode.ofSuccess(); + } + } + + @Override + public CompletableResultCode flush() { + return exporter.flush(); + } + + @Override + public CompletableResultCode shutdown() { + return exporter.shutdown(); + } + + public boolean doEnable() { + setEnabled(true); + return true; + } + + public boolean doDisable() { + setEnabled(false); + return true; + } + +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java new file mode 100644 index 0000000000..9eaebb6ff4 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java @@ -0,0 +1,63 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import lombok.Data; + +/** + * A custom {@link SpanProcessor} wrapper that can be dynamically enabled or disabled. + */ +@Data +public class DynamicallyActivatableSpanProcessor implements SpanProcessor { + + private boolean enabled; + + /** + * The real implementation of the {@link SpanProcessor} + */ + private SpanProcessor spanProcessor; + + private DynamicallyActivatableSpanProcessor(SpanProcessor spanProcessor) { + this.spanProcessor = spanProcessor; + } + + public static DynamicallyActivatableSpanProcessor create(SpanProcessor spanProcessor) { + return new DynamicallyActivatableSpanProcessor(spanProcessor); + } + + public static DynamicallyActivatableSpanProcessor createSimpleSpanProcessor(SpanExporter spanExporter) { + return new DynamicallyActivatableSpanProcessor(SimpleSpanProcessor.create(spanExporter)); + } + + @Override + public void onStart(Context parentContext, ReadWriteSpan span) { + // if enabled, call the real processor's onStart + if (isEnabled()) { + spanProcessor.onStart(parentContext, span); + } + // otherwise, do nothing + } + + @Override + public boolean isStartRequired() { + return spanProcessor.isStartRequired(); + } + + @Override + public void onEnd(ReadableSpan span) { + // if enabled, call the real processor's onEnd + if (isEnabled()) { + spanProcessor.onEnd(span); + } + // otherwise, do nothing + } + + @Override + public boolean isEndRequired() { + return spanProcessor.isEndRequired(); + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java index a761160f78..c623dd7859 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java @@ -1,7 +1,5 @@ package rocks.inspectit.ocelot.core.exporter; -import io.opencensus.exporter.trace.jaeger.JaegerExporterConfiguration; -import io.opencensus.exporter.trace.jaeger.JaegerTraceExporter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -42,8 +40,11 @@ protected boolean doEnable(InspectitConfig configuration) { try { JaegerExporterSettings settings = configuration.getExporters().getTracing().getJaeger(); log.info("Starting Jaeger Exporter with url '{}'", settings.getUrl()); + // TODO re-implement with OTel + /* JaegerTraceExporter.createAndRegister( JaegerExporterConfiguration.builder().setThriftEndpoint(settings.getUrl()).setServiceName(settings.getServiceName()).build()); + */ return true; } catch (Throwable t) { log.error("Error creating Jaeger exporter", t); @@ -55,7 +56,10 @@ protected boolean doEnable(InspectitConfig configuration) { protected boolean doDisable() { log.info("Stopping Jaeger Exporter"); try { + // TODO: reimplement with OTel + /* JaegerTraceExporter.unregister(); + */ } catch (Throwable t) { log.error("Error disabling Jaeger exporter", t); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java new file mode 100644 index 0000000000..7f1b29ab5d --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java @@ -0,0 +1,225 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.extension.trace.propagation.JaegerPropagator; +import io.opentelemetry.opencensusshim.metrics.OpenCensusMetrics; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.metrics.MetricLoggingExporterSettings; +import rocks.inspectit.ocelot.config.model.exporters.trace.TraceLoggingExporterSettings; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; +import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; + +import javax.validation.Valid; + +/** + * Service for the {@link io.opentelemetry.exporter.logging.LoggingMetricExporter} and {@link io.opentelemetry.exporter.logging.LoggingMetricExporter} + */ +@Component +@Slf4j +public class LoggingExporterService extends DynamicallyActivatableService { + + /** + * The {@link OpenTelemetry} + */ + private OpenTelemetry openTelemetry; + + /** + * The {@link SdkTracerProvider} + */ + private SdkTracerProvider tracerProvider; + + /** + * The {@link DynamicallyActivatableSampler} for the {@link #tracerProvider} + */ + private DynamicallyActivatableSampler sampler; + + /** + * The {@link SpanProcessor} of the {@link #spanExporter + * + private SpanProcessor simpleSpanProcessor; + + /** + * The {@link DynamicallyActivatableSpanExporter} for exporting the spans to the log + */ + private DynamicallyActivatableSpanExporter spanExporter; + + /** + * The {@link SdkMeterProvider} + */ + private SdkMeterProvider meterProvider; + + /** + * The {@link DynamicallyActivatableMetricExporter} for exporting metrics to the log + */ + private DynamicallyActivatableMetricExporter metricExporter; + + /** + * The {@link PeriodicMetricReader} for reading metrics to the log + */ + private PeriodicMetricReaderBuilder metricReader; + + /** + * The service name {@link Resource} + */ + private Resource serviceNameResource; + + public LoggingExporterService() { + super("exporters.metrics.logging", "metrics.enabled", "exporters.tracing.logging"); + } + + @Override + protected void init() { + super.init(); + + // create span exporter and span processors + spanExporter = DynamicallyActivatableSpanExporter.createLoggingSpanExporter(); + + // create new metric exporter + metricExporter = DynamicallyActivatableMetricExporter.createLoggingExporter(); + + // close the tracer provider when the JVM is shutting down + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + if (null != tracerProvider) { + tracerProvider.shutdown(); + } + if (null != meterProvider) { + meterProvider.shutdown(); + } + })); + + } + + @Override + protected boolean checkEnabledForConfig(InspectitConfig conf) { + @Valid MetricLoggingExporterSettings metricLogging = conf.getExporters().getMetrics().getLogging(); + @Valid TraceLoggingExporterSettings traceLogging = conf.getExporters().getTracing().getLogging(); + return (traceLogging.isEnabled() || metricLogging.isEnabled()) && conf.getMetrics().isEnabled(); + } + + @Override + protected boolean doEnable(InspectitConfig configuration) { + MetricLoggingExporterSettings metricLogging = configuration.getExporters().getMetrics().getLogging(); + TraceLoggingExporterSettings traceLogging = configuration.getExporters().getTracing().getLogging(); + + try { + + // enable trace logging + if (traceLogging.isEnabled()) { + + // reset GlobalOpenTelemetry + GlobalOpenTelemetry.resetForTest(); + + // create span processors + // SpanProcessors are also shut down when the corresponding TracerProvider is shut down. Thus, we need to create the SpanProcessors each time + simpleSpanProcessor = SimpleSpanProcessor.create(spanExporter); + + // create sampler + sampler = DynamicallyActivatableSampler.createRatio(env.getCurrentConfig() + .getTracing() + .getSampleProbability()); + + // create Resource for the service name + serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, traceLogging.getServiceName())); + + // set up tracer provider + tracerProvider = SdkTracerProvider.builder() + .addSpanProcessor(simpleSpanProcessor) + .setSampler(sampler) + .setResource(serviceNameResource) + .build(); + + // build and register OTel + openTelemetry = OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance()))) + // TODO: do I also need the W3CBaggagePropagator? + // W3CBaggagePropagator.getInstance() + .buildAndRegisterGlobal(); + + // update OC tracer + OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); + + // enable span exporter + spanExporter.doEnable(); + + log.info("Starting LoggingSpanExporter"); + } + + if (metricLogging.isEnabled()) { + // build and register the MeterProvider if null + metricReader = PeriodicMetricReader.builder(metricExporter) + .setInterval(metricLogging.getExportInterval()); + + meterProvider = SdkMeterProvider.builder() + .setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, configuration.getServiceName()))) + .registerMetricReader(OpenCensusMetrics.attachTo(metricReader.newMetricReaderFactory())) + .buildAndRegisterGlobal(); + + // enable the metric exporter + metricExporter.doEnable(); + log.info("Starting LoggingMetricsExporter"); + } + return true; + } catch (Exception e) { + log.error("Failed to start LoggingExporter", e); + return false; + } + + } + + @Override + protected boolean doDisable() { + try { + // disable the span exporter + if (null != spanExporter && null != tracerProvider && !env.getCurrentConfig() + .getExporters() + .getTracing() + .getLogging() + .isEnabled()) { + spanExporter.doDisable(); + tracerProvider.forceFlush(); + tracerProvider.shutdown(); + tracerProvider = null; + log.info("Stopping LoggingSpanExporter"); + } + + if (null != metricExporter && null != meterProvider && !env.getCurrentConfig() + .getExporters() + .getMetrics() + .getLogging() + .isEnabled()) { + if (null != meterProvider) { + // flush all metrics before disabling them + meterProvider.forceFlush(); + meterProvider.shutdown(); + meterProvider = null; + } + metricExporter.doDisable(); + log.info("Stopping LoggingMetricExporter"); + } + return true; + } catch (Exception e) { + log.error("Failed to stop LoggingExporter", e); + return false; + } + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterService.java index db3991633f..02784fa924 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterService.java @@ -1,8 +1,6 @@ package rocks.inspectit.ocelot.core.exporter; import io.opencensus.common.Duration; -import io.opencensus.exporter.metrics.ocagent.OcAgentMetricsExporter; -import io.opencensus.exporter.metrics.ocagent.OcAgentMetricsExporterConfiguration; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -33,12 +31,15 @@ protected boolean doEnable(InspectitConfig configuration) { try { OpenCensusAgentMetricsExporterSettings settings = configuration.getExporters().getMetrics().getOpenCensusAgent(); log.info("Starting OpenCensus Agent Metrics exporter"); + // TODO: implement OTel equivalent + /* OcAgentMetricsExporter.createAndRegister(OcAgentMetricsExporterConfiguration.builder() .setExportInterval(Duration.fromMillis(settings.getExportInterval().toMillis())) .setEndPoint(settings.getAddress()) .setServiceName(settings.getServiceName()) .setUseInsecure(settings.isUseInsecure()) .setRetryInterval(Duration.fromMillis(settings.getReconnectionPeriod().toMillis())).build()); + */ return true; } catch (Throwable t) { log.error("Error creating OpenCensus Agent Metrics exporter", t); diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterService.java index 2308d8ec8b..88c66d8082 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterService.java @@ -1,8 +1,6 @@ package rocks.inspectit.ocelot.core.exporter; import io.opencensus.common.Duration; -import io.opencensus.exporter.trace.ocagent.OcAgentTraceExporter; -import io.opencensus.exporter.trace.ocagent.OcAgentTraceExporterConfiguration; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -31,11 +29,14 @@ protected boolean doEnable(InspectitConfig configuration) { try { OpenCensusAgentTraceExporterSettings settings = configuration.getExporters().getTracing().getOpenCensusAgent(); log.info("Starting OpenCensus Agent Trace exporter"); + // TODO: implement OTel equivalent + /* OcAgentTraceExporter.createAndRegister(OcAgentTraceExporterConfiguration.builder() .setEndPoint(settings.getAddress()) .setServiceName(settings.getServiceName()) .setUseInsecure(settings.isUseInsecure()) .setRetryInterval(Duration.fromMillis(settings.getReconnectionPeriod().toMillis())).build()); + */ return true; } catch (Throwable t) { log.error("Error creating OpenCensus Agent Trace exporter", t); @@ -47,7 +48,8 @@ protected boolean doEnable(InspectitConfig configuration) { protected boolean doDisable() { log.info("Stopping OpenCensus Agent Trace exporter"); try { - OcAgentTraceExporter.unregister(); + // TODO: implement OTel equivalent + // OcAgentTraceExporter.unregister(); } catch (Throwable t) { log.error("Error disabling OpenCensus Agent Trace exporter", t); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java index 64ea14b1c4..62d796cc2c 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java @@ -1,7 +1,6 @@ package rocks.inspectit.ocelot.core.exporter; -import io.opencensus.exporter.stats.prometheus.PrometheusStatsCollector; -import io.opencensus.exporter.stats.prometheus.PrometheusStatsConfiguration; + import io.prometheus.client.exporter.HTTPServer; import lombok.extern.slf4j.Slf4j; import lombok.val; @@ -37,7 +36,8 @@ protected boolean doEnable(InspectitConfig configuration) { String host = config.getHost(); int port = config.getPort(); log.info("Starting Prometheus Exporter on {}:{}", host, port); - PrometheusStatsCollector.createAndRegister(PrometheusStatsConfiguration.builder().setRegistry(defaultRegistry).build()); + // TODO: implement OTel PrometheusStatsCollector + // PrometheusStatsCollector.createAndRegister(PrometheusStatsConfiguration.builder().setRegistry(defaultRegistry).build()); prometheusClient = new HTTPServer(host, port, true); } catch (Exception e) { log.error("Error Starting Prometheus HTTP Endpoint!", e); diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java index 022c2f23dc..b1dff825d2 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java @@ -1,7 +1,6 @@ package rocks.inspectit.ocelot.core.exporter; -import io.opencensus.exporter.trace.zipkin.ZipkinExporterConfiguration; -import io.opencensus.exporter.trace.zipkin.ZipkinTraceExporter; + import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -34,8 +33,11 @@ protected boolean doEnable(InspectitConfig configuration) { try { ZipkinExporterSettings settings = configuration.getExporters().getTracing().getZipkin(); log.info("Starting Zipkin Exporter with url '{}'", settings.getUrl()); + // TODO: implement OTel equivalent + /* ZipkinTraceExporter.createAndRegister( ZipkinExporterConfiguration.builder().setV2Url(settings.getUrl()).setServiceName(settings.getServiceName()).build()); + */ return true; } catch (Throwable t) { log.error("Error creating Zipkin exporter", t); @@ -47,7 +49,10 @@ protected boolean doEnable(InspectitConfig configuration) { protected boolean doDisable() { log.info("Stopping Zipkin Exporter"); try { + // TODO: implement OTel equivalent + /* ZipkinTraceExporter.unregister(); + */ } catch (Throwable t) { log.error("Error disabling Zipkin exporter", t); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextPropagationUtil.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextPropagationUtil.java index 33485a4d5e..4a38159d2b 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextPropagationUtil.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextPropagationUtil.java @@ -4,6 +4,7 @@ import io.opencensus.trace.SpanContext; import io.opencensus.trace.Tracing; import io.opencensus.trace.propagation.TextFormat; +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; import lombok.extern.slf4j.Slf4j; import org.springframework.util.CollectionUtils; import rocks.inspectit.ocelot.config.model.tracing.PropagationFormat; @@ -33,6 +34,7 @@ public class ContextPropagationUtil { * (d is the identifier for "Double") */ private static final Map, Character> TYPE_TO_ID_MAP = new HashMap<>(); + private static final Map> TYPE_ID_TO_PARSER_MAP = new HashMap<>(); private static final String ENCODING_CHARSET = java.nio.charset.StandardCharsets.UTF_8.toString(); @@ -42,7 +44,9 @@ public class ContextPropagationUtil { private static final String B3_HEADER_PREFIX = "X-B3-"; private static final Pattern COMMA_WITH_WHITESPACES = Pattern.compile(" *, *"); + private static final Pattern SEMICOLON_WITH_WHITESPACES = Pattern.compile(" *; *"); + private static final Pattern EQUALS_WITH_WHITESPACES = Pattern.compile(" *= *"); private static final Set PROPAGATION_FIELDS = new HashSet<>(); @@ -58,6 +62,7 @@ public void put(Map carrier, String key, String value) { carrier.put(key, value); } }; + public static final TextFormat.Getter> MAP_EXTRACTOR = new TextFormat.Getter>() { @Override public String get(Map carrier, String key) { @@ -95,6 +100,7 @@ public String get(Map carrier, String key) { * Takes the given key-value pairs and encodes them into the Correlation-Context header. * * @param dataToPropagate the key-value pairs to propagate. + * * @return the result propagation map */ public static Map buildPropagationHeaderMap(Stream> dataToPropagate) { @@ -106,6 +112,7 @@ public static Map buildPropagationHeaderMap(Stream buildPropagationHeaderMap(Stream> dataToPropagate, SpanContext spanToPropagate) { @@ -170,11 +177,16 @@ public static void readPropagatedDataFromHeaderMap(Map propagati * Decodes a span context from the given header to value map into the given target context. * * @param propagationMap the headers to decode + * * @return if the data contained any trace correlation, the SpanContext is returned. Otherwise returns null */ public static SpanContext readPropagatedSpanContextFromHeaderMap(Map propagationMap) { - boolean anyB3Header = Tracing.getPropagationComponent().getB3Format().fields().stream().anyMatch(propagationMap::containsKey); + boolean anyB3Header = Tracing.getPropagationComponent() + .getB3Format() + .fields() + .stream() + .anyMatch(propagationMap::containsKey); if (anyB3Header) { try { return Tracing.getPropagationComponent().getB3Format().extract(propagationMap, MAP_EXTRACTOR); @@ -184,7 +196,11 @@ public static SpanContext readPropagatedSpanContextFromHeaderMap(Map headers) { StringBuilder builder = new StringBuilder("["); if (!CollectionUtils.isEmpty(headers)) { - String headerString = headers.entrySet().stream() + String headerString = headers.entrySet() + .stream() .filter(entry -> entry.getKey().startsWith(B3_HEADER_PREFIX)) .map(entry -> "\"" + entry.getKey() + "\": \"" + entry.getValue() + "\"") .collect(Collectors.joining(", ")); @@ -263,6 +281,7 @@ private static void readCorrelationContext(String correlationContext, InspectitC * * @param stringValue the value to parse * @param properties the collection of property definition in the format "propertyname=value" + * * @return the parsed value */ private static Object parseTyped(String stringValue, Collection properties) { @@ -301,7 +320,7 @@ public static void setPropagationFormat(PropagationFormat format) { log.info("Using Datadog format for context propagation."); propagationFormat = DatadogFormat.INSTANCE; break; - default: + default: log.warn("The specified propagation format {} is not supported. Falling back to B3 format.", format); propagationFormat = Tracing.getPropagationComponent().getB3Format(); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java new file mode 100644 index 0000000000..ba082e4c75 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java @@ -0,0 +1,74 @@ +package rocks.inspectit.ocelot.core.utils; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Tracer; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; + +@Slf4j +public class OpenCensusShimUtils { + + /** + * Updates the {@link io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl#OTEL_TRACER} to the current {@link GlobalOpenTelemetry#getTracer("io.opentelemetry.opencensusshim")} via reflection. + * This method should only be called if {@link GlobalOpenTelemetry#get()} has changed and the {@link io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl#OTEL_TRACER} still references the deprecated {@link Tracer} + * + * @return Whether the OTEL_TRACER was successfully updated + * + * @throws ClassNotFoundException + * @throws NoSuchFieldException + * @throws IllegalAccessException + */ + public static boolean updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl() { + return setOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(GlobalOpenTelemetry.getTracer("io.opentelemetry.opencensusshim")); + } + + /** + * Sets the {@link io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl#OTEL_TRACER} to the given {@link Tracer} via reflection. + * This method should only be called if {@link GlobalOpenTelemetry#get()} has changed and the {@link io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl#OTEL_TRACER} still references the deprecated {@link Tracer} + * + * @param tracer The new {@link Tracer} + * + * @return Whether the OTEL_TRACER was successfully updated + * + * @throws ClassNotFoundException + * @throws NoSuchFieldException + * @throws IllegalAccessException + */ + public static boolean setOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(Tracer tracer) { + Field tracerField = null; + try { + tracerField = Class.forName("io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl") + .getDeclaredField("OTEL_TRACER"); + + + // set static final field + ReflectionUtils.setFinalStatic(tracerField, tracer); + + // set to the current tracer + tracerField.set(null, tracer); + return true; + } catch (Exception e) { + log.error("Failed to set OTEL_TRACER in OpenTelemetrySpanBuilderImpl", e); + return false; + } + } + + public static Tracer getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl() { + Field tracerField = null; + try { + tracerField = Class.forName("io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl") + .getDeclaredField("OTEL_TRACER"); + // make the field accessible + tracerField.setAccessible(true); + + Tracer tracer = (Tracer) tracerField.get(null); + return tracer; + } catch (Exception e) { + log.error("Failed to get OTEL_TRACER of OpenTelemetrySpanBuilderImpl"); + return null; + } + + } + +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java new file mode 100644 index 0000000000..d7bdb2adfb --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java @@ -0,0 +1,39 @@ +package rocks.inspectit.ocelot.core.utils; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public class ReflectionUtils { + + /** + * Makes the {@link Field} accessible and removes the {@link Modifier#FINAL} + * + * @param field + * + * @return The {@link Field} with updated modifiers + * + * @throws NoSuchFieldException + * @throws IllegalAccessException + */ + public static Field makeFieldAccessibleAndRemoveFinal(Field field) throws NoSuchFieldException, IllegalAccessException { + field.setAccessible(true); + + Field modifiers = Field.class.getDeclaredField("modifiers"); + modifiers.setAccessible(true); + modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); + return field; + } + + /** + * Sets the value of the final static {@link Field} via reflection and access modification + * @param field The final static {@link Field} + * @param newValue + * @throws NoSuchFieldException + * @throws IllegalAccessException + */ + public static void setFinalStatic(Field field, Object newValue) throws NoSuchFieldException, IllegalAccessException { + makeFieldAccessibleAndRemoveFinal(field); + field.set(null, newValue); + } + +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterServiceTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterServiceTest.java new file mode 100644 index 0000000000..67eb3ce3c0 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterServiceTest.java @@ -0,0 +1,329 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.github.netmikey.logunit.api.LogCapturer; +import io.opencensus.stats.*; +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; +import rocks.inspectit.ocelot.core.SpringTestBase; +import rocks.inspectit.ocelot.core.config.InspectitEnvironment; +import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; + +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test class for {@link LoggingExporterService} + */ +public class LoggingExporterServiceTest extends SpringTestBase { + + public static final String INSTRUMENTATION_NAME = "rocks.inspectit.ocelot.instrumentation"; + + public static final String INSTRUMENTATION_VERSION = "0.0.1"; + + @RegisterExtension + LogCapturer spanLogs = LogCapturer.create().captureForType(LoggingSpanExporter.class); + + @RegisterExtension + LogCapturer metricLogs = LogCapturer.create().captureForType(LoggingMetricExporter.class); + + @SpyBean + @Autowired + LoggingExporterService service; + + @Autowired + InspectitEnvironment environment; + + @BeforeEach + private void enableService() { + localSwitch(true); + updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.logging.export-interval", environment.getCurrentConfig() + .getExporters() + .getMetrics() + .getLogging() + .getExportInterval()); + }); + } + + private void localSwitch(boolean enabled) { + localSwitchMetrics(enabled); + localSwitchTracing(enabled); + } + + private void localSwitchMetrics(boolean enabled) { + updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.logging.enabled", enabled); + }); + } + + private void localSwitchTracing(boolean enabled) { + updateProperties(props -> { + props.setProperty("inspectit.exporters.tracing.logging.enabled", enabled); + }); + } + + @Nested + class EnableDisable { + + @Test + void testMasterSwitch() throws Exception { + updateProperties(props -> { + props.setProperty("inspectit.metrics.enabled", "false"); + }); + // TODO: test service disabled + assertThat(service.isEnabled()).isFalse(); + } + + @Test + void testLocalSwitch() throws Exception { + localSwitch(false); + assertThat(service.isEnabled()).isFalse(); + } + + @Test + void testLoggingTraceExporterSwitch() { + assertThat(service.isEnabled()).isTrue(); + localSwitchTracing(false); + assertThat(service.isEnabled()).isTrue(); + assertThat(environment.getCurrentConfig().getExporters().getTracing().getLogging().isEnabled()).isFalse(); + localSwitchTracing(true); + assertThat(environment.getCurrentConfig().getExporters().getTracing().getLogging().isEnabled()).isTrue(); + } + + @Test + void testLoggingMetricExporterSwitch() { + assertThat(service.isEnabled()).isTrue(); + localSwitchMetrics(false); + assertThat(service.isEnabled()).isTrue(); + assertThat(environment.getCurrentConfig().getExporters().getMetrics().getLogging().isEnabled()).isFalse(); + localSwitchMetrics(true); + assertThat(environment.getCurrentConfig().getExporters().getMetrics().getLogging().isEnabled()).isTrue(); + } + } + + @Nested + class OpenTelemetryLogging { + + private Tracer getTracer() { + return GlobalOpenTelemetry.getTracer(INSTRUMENTATION_NAME, INSTRUMENTATION_VERSION); + } + + private void makeSpans() { + // start span and nested span + Span parentSpan = getTracer().spanBuilder("openTelemetryParentSpan").startSpan(); + try (Scope scope = parentSpan.makeCurrent()) { + Span childSpan = getTracer().spanBuilder("openTelemetryChildSpan").startSpan(); + try (Scope child = childSpan.makeCurrent()) { + // do sth + } finally { + childSpan.end(); + } + } finally { + parentSpan.end(); + } + } + + @Test + void testOpenTelemetryTraceLogging() throws InterruptedException { + assertThat(service.isEnabled()).isTrue(); + localSwitchMetrics(false); + assertThat(environment.getCurrentConfig().getExporters().getTracing().getLogging().isEnabled()); + + makeSpans(); + + Awaitility.waitAtMost(5, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { + // assert that two traces have been logged + assertThat(spanLogs.getEvents()).hasSize(2); + // and the last contains our 'childOne' + assertThat(spanLogs.getEvents().get(0).getMessage()).contains("openTelemetryChildSpan"); + }); + + // turn off trace exporter + localSwitchTracing(false); + + // wait until everything is flushed + Thread.sleep(500); + + // get number of logged events + int numEvents = spanLogs.size(); + + // make sure no more spans are recorded + Thread.sleep(5000); + assertThat(spanLogs.size()).isEqualTo(numEvents); + + // turn the trace exporter on again + localSwitchTracing(true); + Thread.sleep(1000); + makeSpans(); + // wait until the new spans are exported to the log + Awaitility.waitAtMost(5, TimeUnit.SECONDS) + .pollInterval(2, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(spanLogs.size()).isEqualTo(numEvents + 2)); + + } + + @Test + void testLoggingExporterDisabled() throws InterruptedException { + assertThat(service.isEnabled()).isTrue(); + + // disable exporter service + localSwitch(false); + + assertThat(service.isEnabled()).isFalse(); + + makeSpans(); + + // make sure that no spans were exported + assertThat(spanLogs.getEvents()).hasSize(0); + } + + } + + @Nested + class OpenCensusLogging { + + @Test + void testTracerRestart() { + io.opencensus.trace.Tracer tracer = getTracer(); + localSwitchTracing(false); + Awaitility.waitAtMost(5, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(environment.getCurrentConfig() + .getExporters() + .getTracing() + .getLogging() + .isEnabled()).isFalse()); + localSwitchTracing(true); + OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); + Awaitility.waitAtMost(5, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(environment.getCurrentConfig() + .getExporters() + .getTracing() + .getLogging() + .isEnabled()).isTrue()); + io.opencensus.trace.Tracer newTracer = getTracer(); + + assertThat(tracer).isNotSameAs(newTracer); + + } + + @Test + void testOpenCensusTraceLogging() throws InterruptedException { + assertThat(service.isEnabled()).isTrue(); + // turn off metrics to avoid spamming + localSwitchMetrics(false); + + System.out.println(getTracer().toString() + " - " + getTracer().hashCode()); + // make some spans + makeSpans(); + + // assert that both spans are logged + assertThat(spanLogs.getEvents()).hasSize(2); + assertThat(spanLogs.getEvents().get(0).getMessage()).contains("openCensusChild"); + + int numEvents = spanLogs.size(); + + // turn off tracing exporter + localSwitchTracing(false); + // make sure no more spans are recorded + Thread.sleep(500); + assertThat(spanLogs.size()).isEqualTo(numEvents); + + // turn tracing exporter back on + localSwitchTracing(true); + Thread.sleep(500); + + System.out.println(getTracer().toString() + " - " + getTracer().hashCode()); + // make spans + makeSpans(); + // verify logging of spans + Awaitility.waitAtMost(10, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(spanLogs.size()).isEqualTo(numEvents + 2)); + } + + private io.opencensus.trace.Tracer getTracer() { + return Tracing.getTracer(); + } + + private void makeSpans() { + // get OC tracer and start spans + io.opencensus.trace.Tracer tracer = getTracer(); + + // start span and nested span + try (io.opencensus.common.Scope scope = tracer.spanBuilder("openCensusParent").startScopedSpan()) { + try (io.opencensus.common.Scope childScope = tracer.spanBuilder("openCensusChild").startScopedSpan()) { + io.opencensus.trace.Span span = tracer.getCurrentSpan(); + span.addAnnotation("invoking stuff in openCensusChild"); + } + } + } + + StatsRecorder statsRecorder = Stats.getStatsRecorder(); + + @Test + void testOpenCensusMetricLogging() throws InterruptedException { + // change export interval + updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.logging.export-interval", "500ms"); + }); + assertThat(service.isEnabled()).isTrue(); + // shut the trace exporter off + localSwitchTracing(false); + assertThat(environment.getCurrentConfig().getExporters().getMetrics().getLogging().isEnabled()).isTrue(); + + // capture some metrics + captureOpenCensusMetrics(); + + // wait until the metrics are exported + Awaitility.waitAtMost(15, TimeUnit.SECONDS).pollInterval(2, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(metricLogs.getEvents().size()).isGreaterThan(0); + // assert that the latest metric is our custom log + assertThat(metricLogs.getEvents() + .get(metricLogs.getEvents().size() - 1) + .getArgumentArray()[0].toString()).contains("oc.desc"); + + }); + + // now turn the exporter off and make sure that no more metrics are exported to the log + localSwitchMetrics(false); + // wait until everything is flushed + Thread.sleep(500); + int numEvents = metricLogs.getEvents().size(); + + Thread.sleep(environment.getCurrentConfig() + .getExporters() + .getMetrics() + .getLogging() + .getExportInterval() + .toMillis() + 1000); + assertThat(metricLogs.getEvents().size()).isEqualTo(numEvents); + + } + + private void captureOpenCensusMetrics() { + Measure.MeasureLong measure = Measure.MeasureLong.create("oc.measure", "oc.desc", "oc.unit"); + Stats.getViewManager() + .registerView(View.create(View.Name.create("oc.sum"), "oc.desc", measure, Aggregation.Count.create(), Collections.emptyList())); + + statsRecorder.newMeasureMap().put(measure, 1).record(); + + } + } + +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterServiceIntTest.java deleted file mode 100644 index 246d3d19e8..0000000000 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterServiceIntTest.java +++ /dev/null @@ -1,129 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import com.google.common.util.concurrent.MoreExecutors; -import com.google.errorprone.annotations.concurrent.GuardedBy; -import io.grpc.BindableService; -import io.grpc.Server; -import io.grpc.ServerBuilder; -import io.grpc.netty.NettyServerBuilder; -import io.grpc.stub.StreamObserver; -import io.opencensus.proto.agent.metrics.v1.ExportMetricsServiceRequest; -import io.opencensus.proto.agent.metrics.v1.ExportMetricsServiceResponse; -import io.opencensus.proto.agent.metrics.v1.MetricsServiceGrpc; -import io.opencensus.stats.*; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.TestPropertySource; -import rocks.inspectit.ocelot.core.SpringTestBase; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - -@TestPropertySource(properties = { - "inspectit.exporters.metrics.open-census-agent.address=localhost:55678", - "inspectit.exporters.metrics.open-census-agent.use-insecure=true", -}) -@DirtiesContext -public class OpenCensusAgentMetricsExporterServiceIntTest extends SpringTestBase { - - public static final String HOST = "localhost"; - public static final int PORT = 55678; - private static Server agent; - @Autowired - private StatsRecorder statsRecorder; - private static FakeOcAgentMetricsServiceGrpcImpl fakeOcAgentMetricsServiceGrpc = new FakeOcAgentMetricsServiceGrpcImpl(); - - @BeforeAll - public static void setUp() { - agent = getServer("localhost:55678", fakeOcAgentMetricsServiceGrpc); - } - - @AfterAll - public static void tearDown() { - agent.shutdown(); - } - - /** - * To test the client, a fake GRPC server servers the fake class implementation {@link FakeOcAgentMetricsServiceGrpcImpl}. - */ - //@Test This test is currently deactivated, since the current implementation of the trace exporter tries to connect to a google service before starting - // and the request runs into a timeout. - public void testGrpcRequest() { - try { - agent.start(); - recordDummyMetric(); - } catch (IOException e) { - e.printStackTrace(); - } - await().atMost(15, TimeUnit.SECONDS).untilAsserted(() -> { - assertThat(fakeOcAgentMetricsServiceGrpc.getExportMetricsServiceRequests().size()).isGreaterThan(0); - }); - } - - private void recordDummyMetric() { - MeasureMap mm = statsRecorder.newMeasureMap(); - Measure.MeasureLong measure = Measure.MeasureLong.create("oc-test-metric-name", "metric-description", "unit of metric"); - View view = View.create(View.Name.create("oc-test-metric-name"), "description", measure, Aggregation.Sum.create(), Collections.emptyList()); - Stats.getViewManager().registerView(view); - mm.put(measure, 20); - mm.record(); - } - - private static Server getServer(String endPoint, BindableService service) { - ServerBuilder builder = NettyServerBuilder.forAddress(new InetSocketAddress(HOST, PORT)); - Executor executor = MoreExecutors.directExecutor(); - builder.executor(executor); - return builder.addService(service).build(); - } - - /** - * Based on the integration test of the OpenCensusAgentMetricsExporter (https://github.com/census-instrumentation/opencensus-java/tree/master/exporters/metrics/ocagent/) - */ - static class FakeOcAgentMetricsServiceGrpcImpl extends MetricsServiceGrpc.MetricsServiceImplBase { - - @GuardedBy("this") - private final List exportMetricsServiceRequests = new ArrayList<>(); - - @GuardedBy("this") - private final StreamObserver exportRequestObserver = - new StreamObserver() { - @Override - public void onNext(ExportMetricsServiceRequest value) { - addExportRequest(value); - } - - @Override - public void onError(Throwable t) { - } - - @Override - public void onCompleted() { - } - }; - - @Override - public synchronized StreamObserver export( - StreamObserver responseObserver) { - return exportRequestObserver; - } - - private synchronized void addExportRequest(ExportMetricsServiceRequest request) { - exportMetricsServiceRequests.add(request); - } - - synchronized List getExportMetricsServiceRequests() { - return Collections.unmodifiableList(exportMetricsServiceRequests); - } - } - -} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterServiceIntTest.java deleted file mode 100644 index edc7de2827..0000000000 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterServiceIntTest.java +++ /dev/null @@ -1,207 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import com.google.common.util.concurrent.MoreExecutors; -import com.google.errorprone.annotations.concurrent.GuardedBy; -import io.grpc.BindableService; -import io.grpc.Server; -import io.grpc.ServerBuilder; -import io.grpc.netty.NettyServerBuilder; -import io.grpc.stub.StreamObserver; -import io.opencensus.proto.agent.trace.v1.*; -import io.opencensus.proto.trace.v1.ConstantSampler; -import io.opencensus.proto.trace.v1.TraceConfig; -import io.opencensus.trace.Tracing; -import io.opencensus.trace.samplers.Samplers; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.TestPropertySource; -import rocks.inspectit.ocelot.core.SpringTestBase; - -import javax.annotation.Nullable; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - -@TestPropertySource(properties = { - "inspectit.exporters.tracing.open-census-agent.address=localhost:55678", - "inspectit.exporters.tracing.open-census-agent.use-insecure=true", -}) -@DirtiesContext -public class OpenCensusAgentTraceExporterServiceIntTest extends SpringTestBase { - - public static final String HOST = "localhost"; - public static final int PORT = 55678; - public static final String SPAN_NAME = "ocagentspan"; - private static Server agent; - private static FakeOcAgentTraceServiceGrpcImpl fakeOcAgentTraceServiceGrpc = new FakeOcAgentTraceServiceGrpcImpl(); - - @BeforeAll - public static void setUp() { - agent = getServer("localhost:55678" , fakeOcAgentTraceServiceGrpc); - } - - @AfterAll - public static void tearDown() { - agent.shutdown(); - } - - /** - * To test the client, a fake GRPC server servers the fake class implementation {@link FakeOcAgentTraceServiceGrpcImpl}. - */ - //@Test This test is currently deactivated, since the current implementation of the trace exporter tries to connect to a google service before starting - // and the request runs into a timeout. - public void testGrpcRequest() { - try { - agent.start(); - Tracing.getTracer().spanBuilder(SPAN_NAME) - .setSampler(Samplers.alwaysSample()) - .startSpanAndRun(() -> { - }); - } catch (IOException e) { - e.printStackTrace(); - } - await().atMost(15, TimeUnit.SECONDS).untilAsserted(() -> { - assertThat(fakeOcAgentTraceServiceGrpc.getExportTraceServiceRequests()).anySatisfy((req) -> - assertThat(req.getSpansList()).anySatisfy((span) -> - assertThat(span.getName().getValue()).isEqualTo(SPAN_NAME)) - ); - }); - } - - private static Server getServer(String endPoint, BindableService service) { - ServerBuilder builder = NettyServerBuilder.forAddress(new InetSocketAddress(HOST, PORT)); - Executor executor = MoreExecutors.directExecutor(); - builder.executor(executor); - return builder.addService(service).build(); - } - - /** - * Based on the integration test of the OpenCensusAgentTraceExporter (https://github.com/census-instrumentation/opencensus-java/tree/master/exporters/trace/ocagent) - */ - static class FakeOcAgentTraceServiceGrpcImpl extends TraceServiceGrpc.TraceServiceImplBase { - - // Default updatedLibraryConfig uses an always sampler. - @GuardedBy("this") - private UpdatedLibraryConfig updatedLibraryConfig = - UpdatedLibraryConfig.newBuilder() - .setConfig( - TraceConfig.newBuilder() - .setConstantSampler( - ConstantSampler.newBuilder().setDecision(ConstantSampler.ConstantDecision.ALWAYS_ON).build()) - .build()) - .build(); - - @GuardedBy("this") - private final List currentLibraryConfigs = new ArrayList<>(); - - @GuardedBy("this") - private final CopyOnWriteArrayList exportTraceServiceRequests = new CopyOnWriteArrayList<>(); - - @GuardedBy("this") - private final AtomicReference> configRequestObserverRef = - new AtomicReference<>(); - - @GuardedBy("this") - private final StreamObserver configResponseObserver = - new StreamObserver() { - @Override - public void onNext(CurrentLibraryConfig value) { - addCurrentLibraryConfig(value); - try { - // Do not send UpdatedLibraryConfigs too frequently. - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - sendUpdatedLibraryConfig(); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - resetConfigRequestObserverRef(); - } - - @Override - public void onCompleted() { - resetConfigRequestObserverRef(); - } - }; - - @GuardedBy("this") - private final StreamObserver exportRequestObserver = - new StreamObserver() { - @Override - public void onNext(ExportTraceServiceRequest value) { - addExportRequest(value); - } - - @Override - public void onError(Throwable t) { - t.printStackTrace(); - - } - - @Override - public void onCompleted() { - } - }; - - @GuardedBy("this") - private CountDownLatch countDownLatch; - - @Override - public synchronized StreamObserver config( - StreamObserver updatedLibraryConfigStreamObserver) { - configRequestObserverRef.set(updatedLibraryConfigStreamObserver); - return configResponseObserver; - } - - @Override - public synchronized StreamObserver export( - StreamObserver exportTraceServiceResponseStreamObserver) { - return exportRequestObserver; - } - - private synchronized void addCurrentLibraryConfig(CurrentLibraryConfig currentLibraryConfig) { - if (countDownLatch != null && countDownLatch.getCount() == 0) { - return; - } - currentLibraryConfigs.add(currentLibraryConfig); - } - - private synchronized void addExportRequest(ExportTraceServiceRequest request) { - exportTraceServiceRequests.add(request); - } - - // Returns the stored ExportTraceServiceRequests. - synchronized List getExportTraceServiceRequests() { - return exportTraceServiceRequests; - } - - private synchronized void sendUpdatedLibraryConfig() { - @Nullable - StreamObserver configRequestObserver = configRequestObserverRef.get(); - if (configRequestObserver != null) { - configRequestObserver.onNext(updatedLibraryConfig); - } - if (countDownLatch != null) { - countDownLatch.countDown(); - } - } - - private synchronized void resetConfigRequestObserverRef() { - configRequestObserverRef.set(null); - } - } -} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentMetricsExporterServiceIntTest.java new file mode 100644 index 0000000000..0446690ab4 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentMetricsExporterServiceIntTest.java @@ -0,0 +1,6 @@ +package rocks.inspectit.ocelot.core.exporter; + +// TODO: implement according to the previously used OpenCensusAgentMetricsExporterServiceIntTest +public class OpenTelemetryAgentMetricsExporterServiceIntTest { + +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentTraceExporterServiceIntTest.java new file mode 100644 index 0000000000..8286760676 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentTraceExporterServiceIntTest.java @@ -0,0 +1,6 @@ +package rocks.inspectit.ocelot.core.exporter; + +// TODO: implement this test class according to the previously used OpenCensusAgentTraceExporterServiceIntTest +public class OpenTelemetryAgentTraceExporterServiceIntTest { + +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java index a678dee60a..74af00f51b 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java @@ -1,8 +1,6 @@ package rocks.inspectit.ocelot.core.exporter; import com.github.tomakehurst.wiremock.WireMockServer; -import io.opencensus.exporter.trace.zipkin.ZipkinTraceExporter; -import io.opencensus.impl.trace.TraceComponentImpl; import io.opencensus.trace.Tracing; import io.opencensus.trace.samplers.Samplers; import org.junit.jupiter.api.AfterEach; @@ -21,9 +19,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.awaitility.Awaitility.await; -@TestPropertySource(properties = { - "inspectit.exporters.tracing.zipkin.url=http://127.0.0.1:9411/api/v2/spans" -}) +@TestPropertySource(properties = {"inspectit.exporters.tracing.zipkin.url=http://127.0.0.1:9411/api/v2/spans"}) @DirtiesContext public class ZipkinExporterServiceIntTest extends SpringTestBase { @@ -41,8 +37,7 @@ void setupWiremock() { wireMockServer.start(); configureFor(wireMockServer.port()); - stubFor(post(urlPathEqualTo(ZIPKIN_PATH)) - .willReturn(aResponse().withStatus(200))); + stubFor(post(urlPathEqualTo(ZIPKIN_PATH)).willReturn(aResponse().withStatus(200))); } @AfterEach @@ -52,10 +47,8 @@ void cleanup() { @Test void verifyTraceSent() throws InterruptedException { - Tracing.getTracer().spanBuilder("zipkinspan") - .setSampler(Samplers.alwaysSample()) - .startSpanAndRun(() -> { - }); + Tracing.getTracer().spanBuilder("zipkinspan").setSampler(Samplers.alwaysSample()).startSpanAndRun(() -> { + }); logger.info("Wait for Jaeger to process the span..."); Thread.sleep(1100L); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/OpenCensusShimUtilsTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/OpenCensusShimUtilsTest.java new file mode 100644 index 0000000000..cd6f5e4841 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/OpenCensusShimUtilsTest.java @@ -0,0 +1,47 @@ +package rocks.inspectit.ocelot.core.util; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import org.junit.jupiter.api.Test; +import rocks.inspectit.ocelot.core.SpringTestBase; +import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +public class OpenCensusShimUtilsTest extends SpringTestBase { + + @Test + void testUpdateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl() throws InterruptedException { + Tracer tracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); + + // reset OTEL + GlobalOpenTelemetry.resetForTest(); + + // build and register new OTEL + OpenTelemetrySdk.builder().buildAndRegisterGlobal(); + + // update the OTEL_TRACER + OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); + + Tracer newTracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); + + // assert that the OTEL_TRACER has changed + assertThat(tracer).isNotSameAs(newTracer); + } + + @Test + void testSetOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl() { + // get current OTEL_TRACER + Tracer tracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); + + // set to a different tracer + OpenCensusShimUtils.setOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(GlobalOpenTelemetry.getTracer("this.is.my.tracer")); + + // get new OTEL_TRACER + Tracer newTracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); + + // assert that the OTEL_TRACER has changed + assertThat(tracer).isNotSameAs(newTracer); + } +} From 6ebb7848b8e26c4f70f9c845731c9f630d1fd833 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 10 Dec 2021 11:14:57 +0100 Subject: [PATCH 11/86] Fixed small typo in LoggingExporterService --- .../inspectit/ocelot/core/exporter/LoggingExporterService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java index 7f1b29ab5d..bfb013c3bf 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java @@ -54,7 +54,7 @@ public class LoggingExporterService extends DynamicallyActivatableService { /** * The {@link SpanProcessor} of the {@link #spanExporter - * + */ private SpanProcessor simpleSpanProcessor; /** From ce3c09d2323539d306ec4f9ca34723552618f64d Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 10 Dec 2021 11:18:39 +0100 Subject: [PATCH 12/86] Update LoggingExporterServiceTest.java --- .../ocelot/core/exporter/LoggingExporterServiceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterServiceTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterServiceTest.java index 67eb3ce3c0..cd162714c4 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterServiceTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterServiceTest.java @@ -198,7 +198,7 @@ class OpenCensusLogging { @Test void testTracerRestart() { - io.opencensus.trace.Tracer tracer = getTracer(); + Tracer tracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); localSwitchTracing(false); Awaitility.waitAtMost(5, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) @@ -216,7 +216,7 @@ void testTracerRestart() { .getTracing() .getLogging() .isEnabled()).isTrue()); - io.opencensus.trace.Tracer newTracer = getTracer(); + Tracer newTracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); assertThat(tracer).isNotSameAs(newTracer); From 2bc438f9885354af11305676dacddbe4ae2fd844 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 10 Dec 2021 13:16:10 +0100 Subject: [PATCH 13/86] Minor refactoring (renaming) of classes --- ...ings.java => LoggingMetricsExporterSettings.java} | 2 +- .../exporters/metrics/MetricsExportersSettings.java | 2 +- ...ttings.java => LoggingTraceExporterSettings.java} | 4 ++-- .../exporters/trace/TraceExportersSettings.java | 2 +- .../ocelot/core/exporter/LoggingExporterService.java | 12 ++++++------ 5 files changed, 11 insertions(+), 11 deletions(-) rename inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/{MetricLoggingExporterSettings.java => LoggingMetricsExporterSettings.java} (92%) rename inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/{TraceLoggingExporterSettings.java => LoggingTraceExporterSettings.java} (81%) diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricLoggingExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/LoggingMetricsExporterSettings.java similarity index 92% rename from inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricLoggingExporterSettings.java rename to inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/LoggingMetricsExporterSettings.java index 347b837934..cc46109970 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricLoggingExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/LoggingMetricsExporterSettings.java @@ -11,7 +11,7 @@ */ @Data @NoArgsConstructor -public class MetricLoggingExporterSettings { +public class LoggingMetricsExporterSettings { private boolean enabled; diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java index 6d13169ad7..11ef356dae 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java @@ -22,5 +22,5 @@ public class MetricsExportersSettings { private InfluxExporterSettings influx; @Valid - private MetricLoggingExporterSettings logging; + private LoggingMetricsExporterSettings logging; } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceLoggingExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java similarity index 81% rename from inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceLoggingExporterSettings.java rename to inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java index 9382f9aa16..c59ce169d0 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceLoggingExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java @@ -4,11 +4,11 @@ import lombok.NoArgsConstructor; /** - * Settings for the {@link io.opentelemetry.exporter.logging.LoggingMetricExporter} + * Settings for the {@link io.opentelemetry.exporter.logging.LoggingSpanExporter} */ @Data @NoArgsConstructor -public class TraceLoggingExporterSettings { +public class LoggingTraceExporterSettings { private boolean enabled; diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java index 4b546f2a87..16c2a2009f 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java @@ -19,5 +19,5 @@ public class TraceExportersSettings { private OpenCensusAgentTraceExporterSettings openCensusAgent; @Valid - private TraceLoggingExporterSettings logging; + private LoggingTraceExporterSettings logging; } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java index bfb013c3bf..7756dc3257 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java @@ -23,8 +23,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; -import rocks.inspectit.ocelot.config.model.exporters.metrics.MetricLoggingExporterSettings; -import rocks.inspectit.ocelot.config.model.exporters.trace.TraceLoggingExporterSettings; +import rocks.inspectit.ocelot.config.model.exporters.metrics.LoggingMetricsExporterSettings; +import rocks.inspectit.ocelot.config.model.exporters.trace.LoggingTraceExporterSettings; import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; @@ -110,15 +110,15 @@ protected void init() { @Override protected boolean checkEnabledForConfig(InspectitConfig conf) { - @Valid MetricLoggingExporterSettings metricLogging = conf.getExporters().getMetrics().getLogging(); - @Valid TraceLoggingExporterSettings traceLogging = conf.getExporters().getTracing().getLogging(); + @Valid LoggingMetricsExporterSettings metricLogging = conf.getExporters().getMetrics().getLogging(); + @Valid LoggingTraceExporterSettings traceLogging = conf.getExporters().getTracing().getLogging(); return (traceLogging.isEnabled() || metricLogging.isEnabled()) && conf.getMetrics().isEnabled(); } @Override protected boolean doEnable(InspectitConfig configuration) { - MetricLoggingExporterSettings metricLogging = configuration.getExporters().getMetrics().getLogging(); - TraceLoggingExporterSettings traceLogging = configuration.getExporters().getTracing().getLogging(); + LoggingMetricsExporterSettings metricLogging = configuration.getExporters().getMetrics().getLogging(); + LoggingTraceExporterSettings traceLogging = configuration.getExporters().getTracing().getLogging(); try { From b6b94ffd3cead52e267154c39fb17a13a15c667f Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 10 Dec 2021 13:22:51 +0100 Subject: [PATCH 14/86] Refactored the LoggingExporterService that combined the export of traces and metrics into two separate classes LoggingMetricExporterService and LoggingTraceExporterService. --- .../core/exporter/LoggingExporterService.java | 225 ------------------ .../LoggingMetricExporterService.java | 110 +++++++++ .../exporter/LoggingTraceExporterService.java | 157 ++++++++++++ .../LoggingMetricsExporterServiceTest.java | 153 ++++++++++++ ...a => LoggingTraceExporterServiceTest.java} | 131 +--------- 5 files changed, 432 insertions(+), 344 deletions(-) delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceTest.java rename inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/{LoggingExporterServiceTest.java => LoggingTraceExporterServiceTest.java} (58%) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java deleted file mode 100644 index 7756dc3257..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterService.java +++ /dev/null @@ -1,225 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; -import io.opentelemetry.context.propagation.ContextPropagators; -import io.opentelemetry.context.propagation.TextMapPropagator; -import io.opentelemetry.exporter.logging.LoggingMetricExporter; -import io.opentelemetry.exporter.logging.LoggingSpanExporter; -import io.opentelemetry.extension.trace.propagation.JaegerPropagator; -import io.opentelemetry.opencensusshim.metrics.OpenCensusMetrics; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.trace.SdkTracerProvider; -import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; -import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import rocks.inspectit.ocelot.config.model.InspectitConfig; -import rocks.inspectit.ocelot.config.model.exporters.metrics.LoggingMetricsExporterSettings; -import rocks.inspectit.ocelot.config.model.exporters.trace.LoggingTraceExporterSettings; -import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; -import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; - -import javax.validation.Valid; - -/** - * Service for the {@link io.opentelemetry.exporter.logging.LoggingMetricExporter} and {@link io.opentelemetry.exporter.logging.LoggingMetricExporter} - */ -@Component -@Slf4j -public class LoggingExporterService extends DynamicallyActivatableService { - - /** - * The {@link OpenTelemetry} - */ - private OpenTelemetry openTelemetry; - - /** - * The {@link SdkTracerProvider} - */ - private SdkTracerProvider tracerProvider; - - /** - * The {@link DynamicallyActivatableSampler} for the {@link #tracerProvider} - */ - private DynamicallyActivatableSampler sampler; - - /** - * The {@link SpanProcessor} of the {@link #spanExporter - */ - private SpanProcessor simpleSpanProcessor; - - /** - * The {@link DynamicallyActivatableSpanExporter} for exporting the spans to the log - */ - private DynamicallyActivatableSpanExporter spanExporter; - - /** - * The {@link SdkMeterProvider} - */ - private SdkMeterProvider meterProvider; - - /** - * The {@link DynamicallyActivatableMetricExporter} for exporting metrics to the log - */ - private DynamicallyActivatableMetricExporter metricExporter; - - /** - * The {@link PeriodicMetricReader} for reading metrics to the log - */ - private PeriodicMetricReaderBuilder metricReader; - - /** - * The service name {@link Resource} - */ - private Resource serviceNameResource; - - public LoggingExporterService() { - super("exporters.metrics.logging", "metrics.enabled", "exporters.tracing.logging"); - } - - @Override - protected void init() { - super.init(); - - // create span exporter and span processors - spanExporter = DynamicallyActivatableSpanExporter.createLoggingSpanExporter(); - - // create new metric exporter - metricExporter = DynamicallyActivatableMetricExporter.createLoggingExporter(); - - // close the tracer provider when the JVM is shutting down - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - if (null != tracerProvider) { - tracerProvider.shutdown(); - } - if (null != meterProvider) { - meterProvider.shutdown(); - } - })); - - } - - @Override - protected boolean checkEnabledForConfig(InspectitConfig conf) { - @Valid LoggingMetricsExporterSettings metricLogging = conf.getExporters().getMetrics().getLogging(); - @Valid LoggingTraceExporterSettings traceLogging = conf.getExporters().getTracing().getLogging(); - return (traceLogging.isEnabled() || metricLogging.isEnabled()) && conf.getMetrics().isEnabled(); - } - - @Override - protected boolean doEnable(InspectitConfig configuration) { - LoggingMetricsExporterSettings metricLogging = configuration.getExporters().getMetrics().getLogging(); - LoggingTraceExporterSettings traceLogging = configuration.getExporters().getTracing().getLogging(); - - try { - - // enable trace logging - if (traceLogging.isEnabled()) { - - // reset GlobalOpenTelemetry - GlobalOpenTelemetry.resetForTest(); - - // create span processors - // SpanProcessors are also shut down when the corresponding TracerProvider is shut down. Thus, we need to create the SpanProcessors each time - simpleSpanProcessor = SimpleSpanProcessor.create(spanExporter); - - // create sampler - sampler = DynamicallyActivatableSampler.createRatio(env.getCurrentConfig() - .getTracing() - .getSampleProbability()); - - // create Resource for the service name - serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, traceLogging.getServiceName())); - - // set up tracer provider - tracerProvider = SdkTracerProvider.builder() - .addSpanProcessor(simpleSpanProcessor) - .setSampler(sampler) - .setResource(serviceNameResource) - .build(); - - // build and register OTel - openTelemetry = OpenTelemetrySdk.builder() - .setTracerProvider(tracerProvider) - .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance()))) - // TODO: do I also need the W3CBaggagePropagator? - // W3CBaggagePropagator.getInstance() - .buildAndRegisterGlobal(); - - // update OC tracer - OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); - - // enable span exporter - spanExporter.doEnable(); - - log.info("Starting LoggingSpanExporter"); - } - - if (metricLogging.isEnabled()) { - // build and register the MeterProvider if null - metricReader = PeriodicMetricReader.builder(metricExporter) - .setInterval(metricLogging.getExportInterval()); - - meterProvider = SdkMeterProvider.builder() - .setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, configuration.getServiceName()))) - .registerMetricReader(OpenCensusMetrics.attachTo(metricReader.newMetricReaderFactory())) - .buildAndRegisterGlobal(); - - // enable the metric exporter - metricExporter.doEnable(); - log.info("Starting LoggingMetricsExporter"); - } - return true; - } catch (Exception e) { - log.error("Failed to start LoggingExporter", e); - return false; - } - - } - - @Override - protected boolean doDisable() { - try { - // disable the span exporter - if (null != spanExporter && null != tracerProvider && !env.getCurrentConfig() - .getExporters() - .getTracing() - .getLogging() - .isEnabled()) { - spanExporter.doDisable(); - tracerProvider.forceFlush(); - tracerProvider.shutdown(); - tracerProvider = null; - log.info("Stopping LoggingSpanExporter"); - } - - if (null != metricExporter && null != meterProvider && !env.getCurrentConfig() - .getExporters() - .getMetrics() - .getLogging() - .isEnabled()) { - if (null != meterProvider) { - // flush all metrics before disabling them - meterProvider.forceFlush(); - meterProvider.shutdown(); - meterProvider = null; - } - metricExporter.doDisable(); - log.info("Stopping LoggingMetricExporter"); - } - return true; - } catch (Exception e) { - log.error("Failed to stop LoggingExporter", e); - return false; - } - } -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java new file mode 100644 index 0000000000..41157ecc52 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java @@ -0,0 +1,110 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.opencensusshim.metrics.OpenCensusMetrics; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.metrics.LoggingMetricsExporterSettings; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; + +import javax.validation.Valid; + +/** + * Service for the {@link io.opentelemetry.exporter.logging.LoggingMetricExporter} + */ +@Component +@Slf4j +public class LoggingMetricExporterService extends DynamicallyActivatableService { + + /** + * The {@link SdkMeterProvider} + */ + private SdkMeterProvider meterProvider; + + /** + * The {@link DynamicallyActivatableMetricExporter} for exporting metrics to the log + */ + private DynamicallyActivatableMetricExporter metricExporter; + + /** + * The {@link PeriodicMetricReader} for reading metrics to the log + */ + private PeriodicMetricReaderBuilder metricReader; + + /** + * The service name {@link Resource} + */ + private Resource serviceNameResource; + + public LoggingMetricExporterService() { + super("exporters.metrics.logging", "metrics.enabled"); + } + + @Override + protected void init() { + super.init(); + + // create new metric exporter + metricExporter = DynamicallyActivatableMetricExporter.createLoggingExporter(); + + // close the tracer provider when the JVM is shutting down + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + if (null != meterProvider) { + meterProvider.shutdown(); + } + })); + } + + @Override + protected boolean checkEnabledForConfig(InspectitConfig configuration) { + @Valid LoggingMetricsExporterSettings logging = configuration.getExporters().getMetrics().getLogging(); + return configuration.getMetrics().isEnabled() && logging.isEnabled(); + } + + @Override + protected boolean doEnable(InspectitConfig configuration) { + LoggingMetricsExporterSettings logging = configuration.getExporters().getMetrics().getLogging(); + try { + // build and register the MeterProvider + metricReader = PeriodicMetricReader.builder(metricExporter).setInterval(logging.getExportInterval()); + + meterProvider = SdkMeterProvider.builder() + .setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, configuration.getServiceName()))) + .registerMetricReader(OpenCensusMetrics.attachTo(metricReader.newMetricReaderFactory())) + .buildAndRegisterGlobal(); + + // enable the metric exporter + metricExporter.doEnable(); + log.info("Starting LoggingMetricsExporter"); + return true; + } catch (Exception e) { + log.error("Failed to start LoggingMetricExporter", e); + return false; + } + } + + @Override + protected boolean doDisable() { + try { + if (null != meterProvider) { + // flush all metrics before disabling them + meterProvider.forceFlush(); + meterProvider.shutdown(); + meterProvider = null; + } + metricExporter.doDisable(); + log.info("Stopping LoggingMetricExporter"); + return true; + } catch (Exception e) { + log.error("Failed to stop LoggingMetricExporter", e); + return false; + } + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java new file mode 100644 index 0000000000..76c24f8d78 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java @@ -0,0 +1,157 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.extension.trace.propagation.JaegerPropagator; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.trace.LoggingTraceExporterSettings; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; +import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; + +import javax.validation.Valid; + +/** + * Service for the {@link io.opentelemetry.exporter.logging.LoggingMetricExporter} + */ +@Component +@Slf4j +public class LoggingTraceExporterService extends DynamicallyActivatableService { + + /** + * The {@link OpenTelemetry} + */ + private OpenTelemetry openTelemetry; + + /** + * The {@link SdkTracerProvider} + */ + private SdkTracerProvider tracerProvider; + + /** + * The {@link DynamicallyActivatableSampler} for the {@link #tracerProvider} + */ + private DynamicallyActivatableSampler sampler; + + /** + * The {@link SpanProcessor} of the {@link #spanExporter + */ + private SpanProcessor simpleSpanProcessor; + + /** + * The {@link DynamicallyActivatableSpanExporter< LoggingSpanExporter >} for exporting the spans to the log + */ + private DynamicallyActivatableSpanExporter spanExporter; + + /** + * The service name {@link Resource} + */ + private Resource serviceNameResource; + + public LoggingTraceExporterService() { + super("exporters.tracing.logging", "tracing.enabled"); + } + + @Override + protected void init() { + super.init(); + + // create span exporter and span processors + spanExporter = DynamicallyActivatableSpanExporter.createLoggingSpanExporter(); + + // close the tracer provider when the JVM is shutting down + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + if (null != tracerProvider) { + tracerProvider.shutdown(); + } + })); + } + + @Override + protected boolean checkEnabledForConfig(InspectitConfig configuration) { + @Valid LoggingTraceExporterSettings logging = configuration.getExporters().getTracing().getLogging(); + return configuration.getTracing().isEnabled() && logging.isEnabled(); + + } + + @Override + protected boolean doEnable(InspectitConfig conf) { + LoggingTraceExporterSettings logging = conf.getExporters().getTracing().getLogging(); + try { + + // reset GlobalOpenTelemetry + GlobalOpenTelemetry.resetForTest(); + + // create span processors + // SpanProcessors are also shut down when the corresponding TracerProvider is shut down. Thus, we need to create the SpanProcessors each time + simpleSpanProcessor = SimpleSpanProcessor.create(spanExporter); + + // create sampler + sampler = DynamicallyActivatableSampler.createRatio(env.getCurrentConfig() + .getTracing() + .getSampleProbability()); + + // create Resource for the service name + serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, logging.getServiceName())); + + // set up tracer provider + tracerProvider = SdkTracerProvider.builder() + .addSpanProcessor(simpleSpanProcessor) + .setSampler(sampler) + .setResource(serviceNameResource) + .build(); + + // build and register OTel + openTelemetry = OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance()))) + // TODO: do I also need the W3CBaggagePropagator? + // W3CBaggagePropagator.getInstance() + .buildAndRegisterGlobal(); + + // update OC tracer + OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); + + // enable span exporter + spanExporter.doEnable(); + + log.info("Starting TraceLoggingSpanExporter"); + + return true; + } catch (Exception e) { + log.error("Failed to start TraceLoggingExporter", e); + return false; + } + } + + @Override + protected boolean doDisable() { + try { + // disable the span exporter + if (null != spanExporter && null != tracerProvider) { + spanExporter.doDisable(); + tracerProvider.forceFlush(); + tracerProvider.shutdown(); + tracerProvider = null; + } + log.info("Stopping TraceLoggingSpanExporter"); + return true; + } catch (Exception e) { + log.error("Failed to stop TraceLoggingExporter", e); + return false; + } + } +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceTest.java new file mode 100644 index 0000000000..2128f0cc4b --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceTest.java @@ -0,0 +1,153 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.github.netmikey.logunit.api.LogCapturer; +import io.opencensus.stats.*; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.BoundLongCounter; +import io.opentelemetry.api.metrics.GlobalMeterProvider; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.beans.factory.annotation.Autowired; +import rocks.inspectit.ocelot.core.SpringTestBase; +import rocks.inspectit.ocelot.core.config.InspectitEnvironment; + +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LoggingMetricsExporterServiceTest extends SpringTestBase { + + @RegisterExtension + LogCapturer metricLogs = LogCapturer.create().captureForType(LoggingMetricExporter.class); + + @Autowired + LoggingMetricExporterService service; + + @Autowired + InspectitEnvironment environment; + + @BeforeEach + void enableService() { + localSwitch(true); + } + + private void localSwitch(boolean enabled) { + updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.logging.enabled", enabled); + }); + } + + @Nested + class EnableDisable { + + @Test + void testMasterSwitch() { + updateProperties(props -> { + props.setProperty("inspectit.metrics.enabled", "false"); + }); + assertThat(service.isEnabled()).isFalse(); + } + + @Test + void testLocalSwitch() { + localSwitch(false); + assertThat(service.isEnabled()).isFalse(); + } + } + + @Nested + class OpenTelemetryLogging { + + @Test + void testOpenTelemetryMetricLogging() { + // change export interval + updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.logging.export-interval", "500ms"); + }); + + assertThat(service.isEnabled()).isTrue(); + + // get the meter and create a counter + Meter meter = GlobalMeterProvider.get() + .meterBuilder("rocks.inspectit.ocelot") + .setInstrumentationVersion("0.0.1") + .build(); + LongCounter counter = meter.counterBuilder("processed_jobs") + .setDescription("Processed jobs") + .setUnit("1") + .build(); + BoundLongCounter workCounter = counter.bind(Attributes.of(AttributeKey.stringKey("Key"), "SomeWork")); + + // record counter + workCounter.add(1); + + // verify that the metric has been exported to the log + Awaitility.waitAtMost(15, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(metricLogs.getEvents().size()).isGreaterThan(0); + assertThat(metricLogs.getEvents()).anyMatch(evt -> evt.getArgumentArray() != null && evt.getArgumentArray()[0].toString() + .contains("processed_jobs")); + }); + } + } + + @Nested + class OpenCensusLogging { + + StatsRecorder statsRecorder = Stats.getStatsRecorder(); + + @Test + void testOpenCensusMetricLogging() throws InterruptedException { + // change export interval + updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.logging.export-interval", "500ms"); + }); + assertThat(service.isEnabled()).isTrue(); + + // capture some metrics + captureOpenCensusMetrics(); + + // wait until the metrics are exported + Awaitility.waitAtMost(15, TimeUnit.SECONDS).pollInterval(2, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(metricLogs.getEvents().size()).isGreaterThan(0); + // assert that the latest metric is our custom log + assertThat(metricLogs.getEvents() + .get(metricLogs.getEvents().size() - 1) + .getArgumentArray()[0].toString()).contains("oc.desc"); + + }); + + // now turn the exporter off and make sure that no more metrics are exported to the log + localSwitch(false); + // wait until everything is flushed + Thread.sleep(500); + int numEvents = metricLogs.getEvents().size(); + + Thread.sleep(environment.getCurrentConfig() + .getExporters() + .getMetrics() + .getLogging() + .getExportInterval() + .toMillis() + 1000); + assertThat(metricLogs.getEvents().size()).isEqualTo(numEvents); + + } + + private void captureOpenCensusMetrics() { + Measure.MeasureLong measure = Measure.MeasureLong.create("oc.measure", "oc.desc", "oc.unit"); + Stats.getViewManager() + .registerView(View.create(View.Name.create("oc.sum"), "oc.desc", measure, Aggregation.Count.create(), Collections.emptyList())); + + statsRecorder.newMeasureMap().put(measure, 1).record(); + + } + } + +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterServiceTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceTest.java similarity index 58% rename from inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterServiceTest.java rename to inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceTest.java index cd162714c4..8e196b3f79 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingExporterServiceTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceTest.java @@ -1,13 +1,11 @@ package rocks.inspectit.ocelot.core.exporter; import io.github.netmikey.logunit.api.LogCapturer; -import io.opencensus.stats.*; import io.opencensus.trace.Tracing; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Scope; -import io.opentelemetry.exporter.logging.LoggingMetricExporter; import io.opentelemetry.exporter.logging.LoggingSpanExporter; import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeEach; @@ -15,20 +13,15 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.SpyBean; import rocks.inspectit.ocelot.core.SpringTestBase; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; -import java.util.Collections; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; -/** - * Test class for {@link LoggingExporterService} - */ -public class LoggingExporterServiceTest extends SpringTestBase { +public class LoggingTraceExporterServiceTest extends SpringTestBase { public static final String INSTRUMENTATION_NAME = "rocks.inspectit.ocelot.instrumentation"; @@ -37,40 +30,18 @@ public class LoggingExporterServiceTest extends SpringTestBase { @RegisterExtension LogCapturer spanLogs = LogCapturer.create().captureForType(LoggingSpanExporter.class); - @RegisterExtension - LogCapturer metricLogs = LogCapturer.create().captureForType(LoggingMetricExporter.class); - - @SpyBean @Autowired - LoggingExporterService service; + LoggingTraceExporterService service; @Autowired InspectitEnvironment environment; @BeforeEach - private void enableService() { + void enableService() { localSwitch(true); - updateProperties(props -> { - props.setProperty("inspectit.exporters.metrics.logging.export-interval", environment.getCurrentConfig() - .getExporters() - .getMetrics() - .getLogging() - .getExportInterval()); - }); } private void localSwitch(boolean enabled) { - localSwitchMetrics(enabled); - localSwitchTracing(enabled); - } - - private void localSwitchMetrics(boolean enabled) { - updateProperties(props -> { - props.setProperty("inspectit.exporters.metrics.logging.enabled", enabled); - }); - } - - private void localSwitchTracing(boolean enabled) { updateProperties(props -> { props.setProperty("inspectit.exporters.tracing.logging.enabled", enabled); }); @@ -80,39 +51,18 @@ private void localSwitchTracing(boolean enabled) { class EnableDisable { @Test - void testMasterSwitch() throws Exception { + void testMasterSwitch() { updateProperties(props -> { - props.setProperty("inspectit.metrics.enabled", "false"); + props.setProperty("inspectit.tracing.enabled", "false"); }); - // TODO: test service disabled assertThat(service.isEnabled()).isFalse(); } @Test - void testLocalSwitch() throws Exception { + void testLocalSwitch() { localSwitch(false); assertThat(service.isEnabled()).isFalse(); } - - @Test - void testLoggingTraceExporterSwitch() { - assertThat(service.isEnabled()).isTrue(); - localSwitchTracing(false); - assertThat(service.isEnabled()).isTrue(); - assertThat(environment.getCurrentConfig().getExporters().getTracing().getLogging().isEnabled()).isFalse(); - localSwitchTracing(true); - assertThat(environment.getCurrentConfig().getExporters().getTracing().getLogging().isEnabled()).isTrue(); - } - - @Test - void testLoggingMetricExporterSwitch() { - assertThat(service.isEnabled()).isTrue(); - localSwitchMetrics(false); - assertThat(service.isEnabled()).isTrue(); - assertThat(environment.getCurrentConfig().getExporters().getMetrics().getLogging().isEnabled()).isFalse(); - localSwitchMetrics(true); - assertThat(environment.getCurrentConfig().getExporters().getMetrics().getLogging().isEnabled()).isTrue(); - } } @Nested @@ -140,8 +90,6 @@ private void makeSpans() { @Test void testOpenTelemetryTraceLogging() throws InterruptedException { assertThat(service.isEnabled()).isTrue(); - localSwitchMetrics(false); - assertThat(environment.getCurrentConfig().getExporters().getTracing().getLogging().isEnabled()); makeSpans(); @@ -153,7 +101,7 @@ void testOpenTelemetryTraceLogging() throws InterruptedException { }); // turn off trace exporter - localSwitchTracing(false); + localSwitch(false); // wait until everything is flushed Thread.sleep(500); @@ -166,7 +114,7 @@ void testOpenTelemetryTraceLogging() throws InterruptedException { assertThat(spanLogs.size()).isEqualTo(numEvents); // turn the trace exporter on again - localSwitchTracing(true); + localSwitch(true); Thread.sleep(1000); makeSpans(); // wait until the new spans are exported to the log @@ -199,7 +147,7 @@ class OpenCensusLogging { @Test void testTracerRestart() { Tracer tracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); - localSwitchTracing(false); + localSwitch(false); Awaitility.waitAtMost(5, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) .untilAsserted(() -> assertThat(environment.getCurrentConfig() @@ -207,7 +155,7 @@ void testTracerRestart() { .getTracing() .getLogging() .isEnabled()).isFalse()); - localSwitchTracing(true); + localSwitch(true); OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); Awaitility.waitAtMost(5, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) @@ -225,10 +173,7 @@ void testTracerRestart() { @Test void testOpenCensusTraceLogging() throws InterruptedException { assertThat(service.isEnabled()).isTrue(); - // turn off metrics to avoid spamming - localSwitchMetrics(false); - System.out.println(getTracer().toString() + " - " + getTracer().hashCode()); // make some spans makeSpans(); @@ -239,16 +184,15 @@ void testOpenCensusTraceLogging() throws InterruptedException { int numEvents = spanLogs.size(); // turn off tracing exporter - localSwitchTracing(false); + localSwitch(false); // make sure no more spans are recorded Thread.sleep(500); assertThat(spanLogs.size()).isEqualTo(numEvents); // turn tracing exporter back on - localSwitchTracing(true); + localSwitch(true); Thread.sleep(500); - System.out.println(getTracer().toString() + " - " + getTracer().hashCode()); // make spans makeSpans(); // verify logging of spans @@ -274,56 +218,5 @@ private void makeSpans() { } } - StatsRecorder statsRecorder = Stats.getStatsRecorder(); - - @Test - void testOpenCensusMetricLogging() throws InterruptedException { - // change export interval - updateProperties(props -> { - props.setProperty("inspectit.exporters.metrics.logging.export-interval", "500ms"); - }); - assertThat(service.isEnabled()).isTrue(); - // shut the trace exporter off - localSwitchTracing(false); - assertThat(environment.getCurrentConfig().getExporters().getMetrics().getLogging().isEnabled()).isTrue(); - - // capture some metrics - captureOpenCensusMetrics(); - - // wait until the metrics are exported - Awaitility.waitAtMost(15, TimeUnit.SECONDS).pollInterval(2, TimeUnit.SECONDS).untilAsserted(() -> { - assertThat(metricLogs.getEvents().size()).isGreaterThan(0); - // assert that the latest metric is our custom log - assertThat(metricLogs.getEvents() - .get(metricLogs.getEvents().size() - 1) - .getArgumentArray()[0].toString()).contains("oc.desc"); - - }); - - // now turn the exporter off and make sure that no more metrics are exported to the log - localSwitchMetrics(false); - // wait until everything is flushed - Thread.sleep(500); - int numEvents = metricLogs.getEvents().size(); - - Thread.sleep(environment.getCurrentConfig() - .getExporters() - .getMetrics() - .getLogging() - .getExportInterval() - .toMillis() + 1000); - assertThat(metricLogs.getEvents().size()).isEqualTo(numEvents); - - } - - private void captureOpenCensusMetrics() { - Measure.MeasureLong measure = Measure.MeasureLong.create("oc.measure", "oc.desc", "oc.unit"); - Stats.getViewManager() - .registerView(View.create(View.Name.create("oc.sum"), "oc.desc", measure, Aggregation.Count.create(), Collections.emptyList())); - - statsRecorder.newMeasureMap().put(measure, 1).record(); - - } } - } From a155806877ce1d971c7f7b8dddc121501accdf22 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 10 Dec 2021 13:30:56 +0100 Subject: [PATCH 15/86] Excluded OpenTelemetry and Jackson from the dependency check. OpenTelemetry guarantees Java 8 support and higher Jackson versions are imported with OTel --- inspectit-ocelot-core/build.gradle | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/inspectit-ocelot-core/build.gradle b/inspectit-ocelot-core/build.gradle index 26aaaf3a65..e90ecb847e 100644 --- a/inspectit-ocelot-core/build.gradle +++ b/inspectit-ocelot-core/build.gradle @@ -92,20 +92,7 @@ dependencies { "rocks.inspectit:opencensus-influxdb-exporter:1.2", // bytecode manipulation - "net.bytebuddy:byte-buddy:1.11.15", - - /* - // use older gson version than v2.8.6 (what OC v0.26+ uses), as v2.8.6 does not work with checkDependencyJavaVersions, see https://github.com/google/gson/issues/1627 - "com.google.code.gson:gson:2.8.5!!", - - // use older version of jackson than 2.13.0 (what OTel uses), as v2.13.10 does not work with checkDependencyJavaVersions - "com.fasterxml.jackson.core:jackson-annotations:2.9.10!!", - "com.fasterxml.jackson.core:jackson-core:2.9.10!!", - "com.fasterxml.jackson.core:jackson-databind:2.9.10!!", - - // use opentelemetry-sdk-common without Java9+ code to avoid checkDependencyJavaVersions failures (this package is used by, for example, opentelemetry-exporter-logger) - "io.opentelemetry:opentelemetry-sdk-common:0.11.0!!", - */ + "net.bytebuddy:byte-buddy:1.11.15" ) testImplementation( @@ -141,7 +128,11 @@ dependencies { // use jarCheck to make sure all classes in our dependencies are at maximum in version 1.8 task checkDependencyJavaVersions { - def excludes = ["byte-buddy"] + def excludes = ["byte-buddy", + // exclude OpenTelemetry as they guarantee JDK 8 support + "opentelemetry", + // exclude jackson which is being used by OTel + "jackson"] def jarCheckPath = "$buildDir/jarCheck" outputs.dir jarCheckPath @@ -177,7 +168,7 @@ task generateVersionFile { } // TODO: re-enable once we have excluded OTel -// jar.dependsOn checkDependencyJavaVersions +jar.dependsOn checkDependencyJavaVersions jar.dependsOn generateVersionFile jar { From 537fdccef7e7069dedb4e1bf9410bd4acf715492 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 10 Dec 2021 16:08:11 +0100 Subject: [PATCH 16/86] Fixed WriteSpanAttributesTest when using the opencensus-shim --- .../hook/actions/span/WriteSpanAttributesTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/WriteSpanAttributesTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/WriteSpanAttributesTest.java index 5baafa411e..fc25a6b7ab 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/WriteSpanAttributesTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/WriteSpanAttributesTest.java @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import rocks.inspectit.ocelot.core.instrumentation.context.InspectitContextImpl; import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; @@ -27,8 +28,9 @@ public class WriteSpanAttributesTest { @Mock IObfuscatory obfuscatory; - @Mock - Span span; + @Spy + // get the current span. This is needed when using the opencensus-shim as mocking the span collides with the OTel's implementation of the Span + Span span = Tracing.getTracer().getCurrentSpan(); @BeforeEach void setupMock() { From 2e9527677c194be31d4a48461c8a84f97ee27b51 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 10 Dec 2021 16:09:14 +0100 Subject: [PATCH 17/86] Fixed SetSpanStatusActionTest when using the opencensus-shim --- .../actions/span/SetSpanStatusActionTest.java | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/SetSpanStatusActionTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/SetSpanStatusActionTest.java index e11b86e6c5..c1bbcc6ffb 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/SetSpanStatusActionTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/SetSpanStatusActionTest.java @@ -5,11 +5,14 @@ import io.opencensus.trace.Span; import io.opencensus.trace.Status; import io.opencensus.trace.Tracing; +import io.opentelemetry.context.Context; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import rocks.inspectit.ocelot.core.instrumentation.context.InspectitContextImpl; import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; @@ -25,14 +28,29 @@ public class SetSpanStatusActionTest { @Mock InspectitContextImpl ctx; - @Mock - Span span; + @Spy + // get the current span. This is needed when using the opencensus-shim as mocking the span collides with OTel's implementation (OpenTelemetryNoRecordEventsSpanImpl) of the Span. + // we could also alternatively try to mock SpanImpl such as OpenTelemetrySpanImpl, but I was not able to fix NullpointerExceptions in OpenTelemetrySpanBuilderImpl that way + Span span = Tracing.getTracer().getCurrentSpan(); @BeforeEach void initMocks() { doReturn(ctx).when(executionContext).getInspectitContext(); } + /** + * Verifies that only {@link io.opentelemetry.api.trace.Span#storeInContext(Context)} has been invoked on the given {@link Span} + * + * @param span + */ + static void verifyNoMoreMeaningfulInteractions(Span span) { + // cast the OC span to an OTel span + io.opentelemetry.api.trace.Span otelSpan = (io.opentelemetry.api.trace.Span) span; + // verify that only storeInContext has been invoked + verify(otelSpan).storeInContext(Mockito.any()); + verifyNoMoreInteractions(span); + } + @Nested class Execute { @@ -45,7 +63,7 @@ void nullStatus() { action.execute(executionContext); } - verifyNoMoreInteractions(span); + verifyNoMoreMeaningfulInteractions(span); } @Test @@ -57,10 +75,9 @@ void falseStatus() { action.execute(executionContext); } - verifyNoMoreInteractions(span); + verifyNoMoreMeaningfulInteractions(span); } - @Test void throwableStatus() { SetSpanStatusAction action = new SetSpanStatusAction((ctx) -> new Throwable()); @@ -72,6 +89,9 @@ void throwableStatus() { verify(span).setStatus(eq(Status.UNKNOWN)); verify(span).putAttribute(eq("error"), eq(AttributeValue.booleanAttributeValue(true))); + // storeInContext will also be called on the OTel span + verify((io.opentelemetry.api.trace.Span) (span)).storeInContext(Mockito.any()); + verifyNoMoreInteractions(span); } @@ -84,7 +104,7 @@ void noSpanEntered() { action.execute(executionContext); } - verifyNoMoreInteractions(span); + verifyNoMoreMeaningfulInteractions(span); } } From 3a545848d084c548812ad2018840bc9f756184af Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 10 Dec 2021 16:09:36 +0100 Subject: [PATCH 18/86] Minor refactoring of method names in LoggingMetricsExporterServiceTest and LoggingTraceExporterServiceTest --- .../LoggingMetricsExporterServiceTest.java | 15 ++++++++++----- .../exporter/LoggingTraceExporterServiceTest.java | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceTest.java index 2128f0cc4b..67938c4ca2 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceTest.java @@ -10,6 +10,7 @@ import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.exporter.logging.LoggingMetricExporter; import org.awaitility.Awaitility; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -39,6 +40,11 @@ void enableService() { localSwitch(true); } + @AfterEach + void disableService() { + localSwitch(false); + } + private void localSwitch(boolean enabled) { updateProperties(props -> { props.setProperty("inspectit.exporters.metrics.logging.enabled", enabled); @@ -67,7 +73,7 @@ void testLocalSwitch() { class OpenTelemetryLogging { @Test - void testOpenTelemetryMetricLogging() { + void verifyOpenTelemetryMetricsWritten() { // change export interval updateProperties(props -> { props.setProperty("inspectit.exporters.metrics.logging.export-interval", "500ms"); @@ -104,7 +110,7 @@ class OpenCensusLogging { StatsRecorder statsRecorder = Stats.getStatsRecorder(); @Test - void testOpenCensusMetricLogging() throws InterruptedException { + void verifyOpenCensusMetricsWritten() throws InterruptedException { // change export interval updateProperties(props -> { props.setProperty("inspectit.exporters.metrics.logging.export-interval", "500ms"); @@ -118,9 +124,8 @@ void testOpenCensusMetricLogging() throws InterruptedException { Awaitility.waitAtMost(15, TimeUnit.SECONDS).pollInterval(2, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(metricLogs.getEvents().size()).isGreaterThan(0); // assert that the latest metric is our custom log - assertThat(metricLogs.getEvents() - .get(metricLogs.getEvents().size() - 1) - .getArgumentArray()[0].toString()).contains("oc.desc"); + assertThat(metricLogs.getEvents()).anyMatch(evt -> evt.getArgumentArray() != null && evt.getArgumentArray()[0].toString() + .contains("oc.desc")); }); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceTest.java index 8e196b3f79..d2dc2b4610 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceTest.java @@ -88,7 +88,7 @@ private void makeSpans() { } @Test - void testOpenTelemetryTraceLogging() throws InterruptedException { + void verifyOpenTelemetryTraceSent() throws InterruptedException { assertThat(service.isEnabled()).isTrue(); makeSpans(); @@ -171,7 +171,7 @@ void testTracerRestart() { } @Test - void testOpenCensusTraceLogging() throws InterruptedException { + void verifyOpenCensusTraceSent() throws InterruptedException { assertThat(service.isEnabled()).isTrue(); // make some spans From 45661c798575f23f9e6a821556ce60b5fc42ebdf Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 16 Dec 2021 08:29:41 +0100 Subject: [PATCH 19/86] Adjusted the systemTests in the agent package to work with OpenTelemetry and the OpenCensus-Shim (#1246). --- inspectit-ocelot-agent/build.gradle | 20 +- .../inspectit/ocelot/agent/AgentMain.java | 5 +- .../Log4J2TraceIdAutoInjectorTest.java | 48 ++- .../tracing/AutoTracingTest.java | 21 +- .../tracing/HttpRemoteTracingTest.java | 54 ++- .../instrumentation/tracing/JigsawTest.java | 5 +- .../tracing/TraceSettingsTest.java | 323 +++++++----------- .../tracing/TraceTestBase.java | 35 +- .../inspectit/ocelot/utils/TestUtils.java | 70 +++- .../ocelot/config/model/InspectitConfig.java | 4 +- inspectit-ocelot-core/build.gradle | 2 + .../autotracing/StackTraceSampler.java | 4 +- .../log/LogTraceCorrelatorImpl.java | 10 +- .../span/ContinueOrStartSpanAction.java | 12 +- ...LoggingMetricsExporterServiceIntTest.java} | 2 +- ...> LoggingTraceExporterServiceIntTest.java} | 6 +- 16 files changed, 302 insertions(+), 319 deletions(-) rename inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/{LoggingMetricsExporterServiceTest.java => LoggingMetricsExporterServiceIntTest.java} (98%) rename inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/{LoggingTraceExporterServiceTest.java => LoggingTraceExporterServiceIntTest.java} (96%) diff --git a/inspectit-ocelot-agent/build.gradle b/inspectit-ocelot-agent/build.gradle index 9419089dd5..306eaf8dc5 100644 --- a/inspectit-ocelot-agent/build.gradle +++ b/inspectit-ocelot-agent/build.gradle @@ -31,7 +31,8 @@ dependencies { opentelemetry( "io.opentelemetry:opentelemetry-sdk:${openTelemetryVersion}", - "io.opentelemetry:opentelemetry-opencensus-shim:${openTelemetryAlphaVersion}" + "io.opentelemetry:opentelemetry-opencensus-shim:${openTelemetryAlphaVersion}", + "io.opencensus:opencensus-impl:${openCensusVersion}", ) annotationProcessor 'org.projectlombok:lombok:1.18.12' @@ -95,8 +96,13 @@ configurations { dependencies { systemTestCompileOnly( - "io.opentelemetry:opentelemetry-sdk:${openTelemetryVersion}", - "io.opentelemetry:opentelemetry-opencensus-shim:${openTelemetryAlphaVersion}", + platform("io.opentelemetry:opentelemetry-bom:${openTelemetryVersion}"), + platform("io.opentelemetry:opentelemetry-bom-alpha:${openTelemetryAlphaVersion}"), + "io.opentelemetry:opentelemetry-sdk", + "io.opentelemetry:opentelemetry-opencensus-shim", + "io.opencensus:opencensus-impl:${openCensusVersion}", + + project(':inspectit-ocelot-bootstrap') ) @@ -104,6 +110,12 @@ dependencies { "io.opencensus:opencensus-testing:${openCensusVersion}", "io.opencensus:opencensus-api:${openCensusVersion}", + // for InMemorySpanExporter + platform("io.opentelemetry:opentelemetry-bom:${openTelemetryVersion}"), + "io.opentelemetry:opentelemetry-sdk-testing", + "io.opentelemetry:opentelemetry-semconv", + "io.opentelemetry:opentelemetry-exporter-logging", + 'org.junit.jupiter:junit-jupiter-engine:5.3.1', 'org.junit.jupiter:junit-jupiter-api:5.3.1', @@ -214,7 +226,7 @@ task systemTest { jvmArgs "-Dinspectit.config.file-based.path=$projectDir/src/system-test/resources/config" // make inspectIT scan the workdir for configs - jvmArgs "-Dinspectit.publishOpenTelemetryToBootstrap=true" // make inspectIT push OTel to the bootstrap + jvmArgs "-Dinspectit.publishOpenTelemetryToBootstrap=true" // make inspectIT push OC to the bootstrap jvmArgs jacoco.asJvmArg + ",inclbootstrapclasses=true" // JaCoCo agent first + bootstrap instrumentation jvmArgs "-javaagent:${agentJarPath}" // Our agent second. diff --git a/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java b/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java index 50f13a2d28..ef20b330ee 100644 --- a/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java +++ b/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java @@ -61,20 +61,17 @@ public static void agentmain(String agentArgs, Instrumentation inst) { } public static void premain(String agentArgs, Instrumentation inst) { - boolean loadOpenTelemetryJarToBootstrap = null != System.getProperty(PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY) ? "true".equalsIgnoreCase(PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY) : "true".equalsIgnoreCase(System.getProperty(PUBLISH_OPEN_TELEMETRY_TO_BOOTSTRAP_PROPERTY)); + boolean loadOpenTelemetryJarToBootstrap = null != System.getProperty(PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY) ? "true".equalsIgnoreCase(System.getProperty(PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY)) : "true".equalsIgnoreCase(System.getProperty(PUBLISH_OPEN_TELEMETRY_TO_BOOTSTRAP_PROPERTY)); // check for deprecated JVM property if (null != System.getProperty(PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY)) { LOGGER.warning("You are using the deprecated JVM property '" + PUBLISH_OPEN_CENSUS_TO_BOOTSTRAP_PROPERTY + "'. Please use the new JVM property '" + PUBLISH_OPEN_TELEMETRY_TO_BOOTSTRAP_PROPERTY + "'. inspectIT Ocelot has moved from OpenCensus to OpenTelemetry. However, applications using the OpenCensusAPI are still supported through the opentelemetry-opencensus-shim "); } - // retrieve and build settings for the telemetry implementation - TelemetrySettings telemetrySettings = new TelemetrySettings(TelemetryImplementation.OPEN_TELEMETRY, loadOpenTelemetryJarToBootstrap, PUBLISH_OPEN_TELEMETRY_TO_BOOTSTRAP_PROPERTY); try { if (loadOpenTelemetryJarToBootstrap) { Path telJarFile = copyResourceToTempJarFile(OPEN_TELEMETRY_FAT_JAR_PATH); inst.appendToBootstrapClassLoaderSearch(new JarFile(telJarFile.toFile())); } - //we make sure that the startup of inspectIT is asynchronous new Thread(() -> startAgent(agentArgs, inst, !loadOpenTelemetryJarToBootstrap)).start(); } catch (Exception e) { diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java index 011e2adf03..774ba6a2e6 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java @@ -12,8 +12,6 @@ import rocks.inspectit.ocelot.logging.Log4J2LoggingRecorder; import rocks.inspectit.ocelot.utils.TestUtils; -import java.util.concurrent.TimeUnit; - import static org.assertj.core.api.Assertions.assertThat; public class Log4J2TraceIdAutoInjectorTest extends InstrumentationSysTestBase { @@ -25,6 +23,13 @@ public static void waitForInstrumentation() { TestUtils.waitForClassInstrumentations(AbstractMessageFactory.class, MessageFactory.class); } + // TODO: maybe remove this method once the OTelController has been implemented + @BeforeAll + public static void initializeOpenTelemetry() { + // initialize OTel as we are otherwise not exporting any traces. + TestUtils.initializeOpenTelemetryForSystemTesting(); + } + @Test public void logStringAndTraceExists() { Log4J2LoggingRecorder.loggingEvents.clear(); @@ -37,12 +42,10 @@ public void logStringAndTraceExists() { LOGGER.info(message, "Log4J2"); } - assertThat(Log4J2LoggingRecorder.loggingEvents) - .hasSize(1); + assertThat(Log4J2LoggingRecorder.loggingEvents).hasSize(1); - assertThat(Log4J2LoggingRecorder.loggingEvents.get(0)) - .extracting(event -> event.getMessage().getFormattedMessage()) - .isEqualTo("[TraceID: " + traceId + "] This is a traced String in Log4J2."); + assertThat(Log4J2LoggingRecorder.loggingEvents.get(0)).extracting(event -> event.getMessage() + .getFormattedMessage()).isEqualTo("[TraceID: " + traceId + "] This is a traced String in Log4J2."); } @Test @@ -57,11 +60,10 @@ public void logCharSequenceAndTraceExists() { LOGGER.info(message); } - assertThat(Log4J2LoggingRecorder.loggingEvents) - .hasSize(1); + assertThat(Log4J2LoggingRecorder.loggingEvents).hasSize(1); - assertThat(Log4J2LoggingRecorder.loggingEvents.get(0)) - .extracting(event -> event.getMessage().getFormattedMessage()) + assertThat(Log4J2LoggingRecorder.loggingEvents.get(0)).extracting(event -> event.getMessage() + .getFormattedMessage()) .isEqualTo("[TraceID: " + traceId + "] This is a traced CharSequence in Log4J2."); } @@ -77,12 +79,10 @@ public void logObjectAndTraceExists() { LOGGER.info(message); } - assertThat(Log4J2LoggingRecorder.loggingEvents) - .hasSize(1); + assertThat(Log4J2LoggingRecorder.loggingEvents).hasSize(1); - assertThat(Log4J2LoggingRecorder.loggingEvents.get(0)) - .extracting(event -> event.getMessage().getFormattedMessage()) - .isEqualTo("[TraceID: " + traceId + "] This is a traced Object in Log4J2."); + assertThat(Log4J2LoggingRecorder.loggingEvents.get(0)).extracting(event -> event.getMessage() + .getFormattedMessage()).isEqualTo("[TraceID: " + traceId + "] This is a traced Object in Log4J2."); } @Test @@ -97,12 +97,10 @@ public void logNullAndTraceExists() { LOGGER.info(message); } - assertThat(Log4J2LoggingRecorder.loggingEvents) - .hasSize(1); + assertThat(Log4J2LoggingRecorder.loggingEvents).hasSize(1); - assertThat(Log4J2LoggingRecorder.loggingEvents.get(0)) - .extracting(event -> event.getMessage().getFormattedMessage()) - .isEqualTo("[TraceID: " + traceId + "] null"); + assertThat(Log4J2LoggingRecorder.loggingEvents.get(0)).extracting(event -> event.getMessage() + .getFormattedMessage()).isEqualTo("[TraceID: " + traceId + "] null"); } @Test @@ -112,11 +110,9 @@ public void traceNotExists() { LOGGER.info(message, "Log4J2"); - assertThat(Log4J2LoggingRecorder.loggingEvents) - .hasSize(1); + assertThat(Log4J2LoggingRecorder.loggingEvents).hasSize(1); - assertThat(Log4J2LoggingRecorder.loggingEvents.get(0)) - .extracting(event -> event.getMessage().getFormattedMessage()) - .isEqualTo("This is Log4J2."); + assertThat(Log4J2LoggingRecorder.loggingEvents.get(0)).extracting(event -> event.getMessage() + .getFormattedMessage()).isEqualTo("This is Log4J2."); } } diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/AutoTracingTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/AutoTracingTest.java index 68377d291b..3289fe89d5 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/AutoTracingTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/AutoTracingTest.java @@ -1,6 +1,6 @@ package rocks.inspectit.ocelot.instrumentation.tracing; -import io.opencensus.trace.export.SpanData; +import io.opentelemetry.sdk.trace.data.SpanData; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import rocks.inspectit.ocelot.utils.TestUtils; @@ -18,9 +18,9 @@ static void waitForInstrumentation() { TestUtils.waitForClassInstrumentation(AutoTracingTest.class, true, 30, TimeUnit.SECONDS); } - SpanData getSpanWithName(Collection spans, String name) { - Optional spanOptional = spans.stream() - .filter(s -> ((SpanData) s).getName().equals(name)) + io.opentelemetry.sdk.trace.data.SpanData getSpanWithName(Collection spans, String name) { + Optional spanOptional = spans.stream() + .filter(s -> s.getName().equals(name)) .findFirst(); assertThat(spanOptional).isNotEmpty(); return spanOptional.get(); @@ -32,14 +32,13 @@ void verifyStackTraceSampling() { assertTraceExported((spans) -> { - SpanData root = getSpanWithName(spans, "AutoTracingTest.instrumentMe"); - SpanData activeWait = getSpanWithName(spans, "*AutoTracingTest.activeWait"); - SpanData passiveWait = getSpanWithName(spans, "*Thread.sleep"); + io.opentelemetry.sdk.trace.data.SpanData root = getSpanWithName(spans, "AutoTracingTest.instrumentMe"); + io.opentelemetry.sdk.trace.data.SpanData activeWait = getSpanWithName(spans, "*AutoTracingTest.activeWait"); + io.opentelemetry.sdk.trace.data.SpanData passiveWait = getSpanWithName(spans, "*Thread.sleep"); + assertThat(activeWait.getParentSpanId()).isEqualTo(root.getSpanId()); + assertThat(passiveWait.getParentSpanId()).isEqualTo(root.getSpanId()); - assertThat(activeWait.getParentSpanId()).isEqualTo(root.getContext().getSpanId()); - assertThat(passiveWait.getParentSpanId()).isEqualTo(root.getContext().getSpanId()); - - assertThat(activeWait.getEndTimestamp()).isLessThan(passiveWait.getStartTimestamp()); + assertThat(activeWait.getEndEpochNanos()).isLessThan(passiveWait.getEndEpochNanos()); }); } diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java index 7cc88adcef..887055c294 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java @@ -1,6 +1,7 @@ package rocks.inspectit.ocelot.instrumentation.tracing; -import io.opencensus.trace.Span; +import io.opentelemetry.api.trace.SpanId; +import io.opentelemetry.api.trace.SpanKind; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.utils.URIUtils; @@ -89,19 +90,15 @@ void testPropagationViaServlet() throws Exception { TestUtils.waitForClassInstrumentation(Class.forName("sun.net.www.protocol.http.HttpURLConnection"), true, 30, TimeUnit.SECONDS); clientSpan(); - - assertTraceExported((spans) -> - assertThat(spans) - .anySatisfy((sp) -> { - assertThat(sp.getName()).endsWith("HttpUrlConnectionTest.clientSpan"); - assertThat(sp.getKind()).isEqualTo(Span.Kind.CLIENT); - assertThat(sp.getParentSpanId()).isNull(); - }) - .anySatisfy((sp) -> { - assertThat(sp.getName()).endsWith("TracingServlet.myHandler"); - assertThat(sp.getKind()).isEqualTo(Span.Kind.SERVER); - assertThat(sp.getParentSpanId()).isNotNull(); - }) + assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { + assertThat(sp.getName()).endsWith("HttpUrlConnectionTest.clientSpan"); + assertThat(sp.getKind()).isEqualTo(SpanKind.CLIENT); + assertThat(sp.getParentSpanId()).isNull(); + }).anySatisfy((sp) -> { + assertThat(sp.getName()).endsWith("TracingServlet.myHandler"); + assertThat(sp.getKind()).isEqualTo(SpanKind.SERVER); + assertThat(sp.getParentSpanId()).isNotNull(); + }) ); @@ -131,25 +128,20 @@ void clientSpan() { @Test void testPropagationViaServlet() throws Exception { - TestUtils.waitForClassInstrumentations(Arrays.asList( - CloseableHttpClient.class, - Class.forName("org.apache.http.impl.client.InternalHttpClient"), - ApacheClientConnectionTest.class, - TracingServlet.class), true, 15, TimeUnit.SECONDS); + TestUtils.waitForClassInstrumentations(Arrays.asList(CloseableHttpClient.class, Class.forName("org.apache.http.impl.client.InternalHttpClient"), ApacheClientConnectionTest.class, TracingServlet.class), true, 15, TimeUnit.SECONDS); clientSpan(); - assertTraceExported((spans) -> - assertThat(spans) - .anySatisfy((sp) -> { - assertThat(sp.getName()).endsWith("ApacheClientConnectionTest.clientSpan"); - assertThat(sp.getKind()).isEqualTo(Span.Kind.CLIENT); - assertThat(sp.getParentSpanId()).isNull(); - }) - .anySatisfy((sp) -> { - assertThat(sp.getName()).endsWith("TracingServlet.myHandler"); - assertThat(sp.getKind()).isEqualTo(Span.Kind.SERVER); - assertThat(sp.getParentSpanId()).isNotNull(); - }) + // TODO: these tests fail as the spans are not belonging to the same trace + + assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { + assertThat(sp.getName()).endsWith("ApacheClientConnectionTest.clientSpan"); + assertThat(sp.getKind()).isEqualTo(SpanKind.CLIENT); + assertThat(SpanId.isValid(sp.getParentSpanId())).isFalse(); + }).anySatisfy((sp) -> { + assertThat(sp.getName()).endsWith("TracingServlet.myHandler"); + assertThat(sp.getKind()).isEqualTo(SpanKind.SERVER); + assertThat(SpanId.isValid(sp.getParentSpanId())).isTrue(); + }) ); diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/JigsawTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/JigsawTest.java index eeda0532b4..1b3a9625da 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/JigsawTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/JigsawTest.java @@ -1,6 +1,6 @@ package rocks.inspectit.ocelot.instrumentation.tracing; -import io.opencensus.trace.AttributeValue; +import io.opentelemetry.api.common.AttributeKey; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import rocks.inspectit.ocelot.utils.TestUtils; @@ -23,8 +23,7 @@ void verifySqlActionInjected() { assertSpansExported(spans -> { assertThat(spans).anySatisfy((span) -> { assertThat(span.getName()).isEqualTo("DriverManager.getDrivers"); - assertThat(span.getAttributes().getAttributeMap().get("test")) - .isEqualTo(AttributeValue.stringAttributeValue("success")); + assertThat(span.getAttributes().asMap().get(AttributeKey.stringKey("test"))).isEqualTo("success"); }); }); } diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceSettingsTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceSettingsTest.java index bb2565b5b5..78c7dc5137 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceSettingsTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceSettingsTest.java @@ -1,8 +1,9 @@ package rocks.inspectit.ocelot.instrumentation.tracing; -import io.opencensus.trace.AttributeValue; import io.opencensus.trace.Status; -import io.opencensus.trace.export.SpanData; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanId; +import io.opentelemetry.sdk.trace.data.SpanData; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -26,21 +27,17 @@ void testAttributeWritingToParentSpan() { TestUtils.waitForClassInstrumentation(TraceSettingsTest.class, true, 15, TimeUnit.SECONDS); attributesSetter(); - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(1) - .anySatisfy((sp) -> { - assertThat(sp.getName()).endsWith("TraceSettingsTest.attributesSetter"); - assertThat(sp.getAttributes().getAttributeMap()) - .hasSize(7) - .containsEntry("entry", AttributeValue.stringAttributeValue("const")) - .containsEntry("exit", AttributeValue.stringAttributeValue("Hello A!")) - .containsEntry("toObfuscate", AttributeValue.stringAttributeValue("***")) - .containsEntry("anything", AttributeValue.stringAttributeValue("***")) - // plus include all common tags (service + key validation only) - .containsEntry("service", AttributeValue.stringAttributeValue("systemtest")) - .containsKeys("host", "host_address"); - }) + assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { + assertThat(sp.getName()).endsWith("TraceSettingsTest.attributesSetter"); + assertThat(sp.getAttributes().asMap()).hasSize(7) + .containsEntry(AttributeKey.stringKey("entry"), "const") + .containsEntry(AttributeKey.stringKey("exit"), "Hello A!") + .containsEntry(AttributeKey.stringKey("toObfuscate"), "***") + .containsEntry(AttributeKey.stringKey("anything"), "***") + // plus include all common tags (service + key validation only) + .containsEntry(AttributeKey.stringKey("service"), "systemtest") + .containsKeys(AttributeKey.stringKey("host"), AttributeKey.stringKey("host_address")); + }) ); @@ -57,29 +54,21 @@ void testConditionalAttributeWriting() { attributesSetterWithConditions(false); attributesSetterWithConditions(true); - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(1) - .anySatisfy((sp) -> { - assertThat(sp.getName()).endsWith("TraceSettingsTest.attributesSetterWithConditions"); - assertThat(sp.getAttributes().getAttributeMap()) - .hasSize(3) - .containsKeys("service", "host", "host_address"); - }) + assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { + assertThat(sp.getName()).endsWith("TraceSettingsTest.attributesSetterWithConditions"); + assertThat(sp.getAttributes().asMap()).hasSize(3) + .containsKeys(AttributeKey.stringKey("service"), AttributeKey.stringKey("host"), AttributeKey.stringKey("host_address")); + }) ); - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(1) - .anySatisfy((sp) -> { - assertThat(sp.getName()).endsWith("TraceSettingsTest.attributesSetterWithConditions"); - assertThat(sp.getAttributes().getAttributeMap()) - .hasSize(5) - .containsEntry("entry", AttributeValue.stringAttributeValue("const")) - .containsEntry("exit", AttributeValue.stringAttributeValue("Hello B!")) - .containsKeys("service", "host", "host_address"); - }) + assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { + assertThat(sp.getName()).endsWith("TraceSettingsTest.attributesSetterWithConditions"); + assertThat(sp.getAttributes().asMap()).hasSize(5) + .containsEntry(AttributeKey.stringKey("entry"), "const") + .containsEntry(AttributeKey.stringKey("exit"), "Hello B!") + .containsKeys(AttributeKey.stringKey("service"), AttributeKey.stringKey("host"), AttributeKey.stringKey("host_address")); + }) ); @@ -99,26 +88,19 @@ void testConditionalSpanCreation() { conditionalRoot(false); conditionalRoot(true); - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(1) - .anySatisfy((sp) -> { - assertThat(sp.getName()).endsWith("TraceSettingsTest.nestedC"); - assertThat(sp.getParentSpanId()).isNull(); - }) + assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { + assertThat(sp.getName()).endsWith("TraceSettingsTest.nestedC"); + assertThat(SpanId.isValid(sp.getParentSpanId())).isFalse(); + }) ); - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(2) - .anySatisfy((sp) -> { - assertThat(sp.getName()).endsWith("TraceSettingsTest.conditionalRoot"); - assertThat(sp.getParentSpanId()).isNull(); - }) - .anySatisfy((sp) -> { - assertThat(sp.getName()).endsWith("TraceSettingsTest.nestedC"); - assertThat(sp.getParentSpanId()).isNotNull(); - }) + assertTraceExported((spans) -> assertThat(spans).hasSize(2).anySatisfy((sp) -> { + assertThat(sp.getName()).endsWith("TraceSettingsTest.conditionalRoot"); + assertThat(SpanId.isValid(sp.getParentSpanId())).isFalse(); + }).anySatisfy((sp) -> { + assertThat(sp.getName()).endsWith("TraceSettingsTest.nestedC"); + assertThat(SpanId.isValid(sp.getParentSpanId())).isTrue(); + }) ); @@ -137,17 +119,13 @@ void testSpanNameCustomization() { TestUtils.waitForClassInstrumentation(TraceSettingsTest.class, true, 15, TimeUnit.SECONDS); namedA("first"); - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(2) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("first"); - assertThat(sp.getParentSpanId()).isNull(); - }) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("second"); - assertThat(sp.getParentSpanId()).isNotNull(); - }) + assertTraceExported((spans) -> assertThat(spans).hasSize(2).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("first"); + assertThat(SpanId.isValid(sp.getParentSpanId())).isFalse(); + }).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("second"); + assertThat(SpanId.isValid(sp.getParentSpanId())).isTrue(); + }) ); @@ -159,17 +137,13 @@ void testNoCommonTagsOnChild() { namedA("whatever"); - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(2) - .anySatisfy((sp) -> { - assertThat(sp.getParentSpanId()).isNull(); - assertThat(sp.getAttributes().getAttributeMap()).hasSize(3); - }) - .anySatisfy((sp) -> { - assertThat(sp.getParentSpanId()).isNotNull(); - assertThat(sp.getAttributes().getAttributeMap()).hasSize(0); - }) + assertTraceExported((spans) -> assertThat(spans).hasSize(2).anySatisfy((sp) -> { + assertThat(SpanId.isValid(sp.getParentSpanId())).isFalse(); + assertThat(sp.getAttributes().asMap()).hasSize(3); + }).anySatisfy((sp) -> { + assertThat(SpanId.isValid(sp.getParentSpanId())).isTrue(); + assertThat(sp.getAttributes().asMap()).hasSize(0); + }) ); @@ -201,37 +175,35 @@ void testInterleavedAsyncSpans() throws Exception { second.doAsync(null, null, "b3", true); assertSpansExported(spans -> { - List asyncSpans = spans.stream() + List asyncSpans = spans.stream() .filter(s -> s.getName().equals("AsyncTask.doAsync")) .collect(Collectors.toList()); assertThat(asyncSpans).hasSize(2); - SpanData firstSpan = asyncSpans.get(0); - SpanData secondSpan = asyncSpans.get(1); + io.opentelemetry.sdk.trace.data.SpanData firstSpan = asyncSpans.get(0); + io.opentelemetry.sdk.trace.data.SpanData secondSpan = asyncSpans.get(1); //order the spans by time - if (secondSpan.getStartTimestamp().compareTo(secondSpan.getStartTimestamp()) < 0) { + if (secondSpan.getStartEpochNanos() - firstSpan.getStartEpochNanos() < 0) { SpanData temp = firstSpan; firstSpan = secondSpan; secondSpan = temp; } //ensure that all method invocations have been combined to single spans - assertThat(firstSpan.getAttributes().getAttributeMap()) - .hasSize(5) - .containsEntry("1", AttributeValue.stringAttributeValue("a1")) - .containsEntry("2", AttributeValue.stringAttributeValue("a2")) - .containsKeys("service", "host", "host_address"); - assertThat(secondSpan.getAttributes().getAttributeMap()) - .hasSize(6) - .containsEntry("1", AttributeValue.stringAttributeValue("b1")) - .containsEntry("2", AttributeValue.stringAttributeValue("b2")) - .containsEntry("3", AttributeValue.stringAttributeValue("b3")) - .containsKeys("service", "host", "host_address"); + assertThat(firstSpan.getAttributes().asMap()).hasSize(5) + .containsEntry(AttributeKey.stringKey("1"), "a1") + .containsEntry(AttributeKey.stringKey("2"), "a2") + .containsKeys(AttributeKey.stringKey("service"), AttributeKey.stringKey("host"), AttributeKey.stringKey("host_address")); + assertThat(secondSpan.getAttributes().asMap()).hasSize(6) + .containsEntry(AttributeKey.stringKey("1"), "b1") + .containsEntry(AttributeKey.stringKey("2"), "b2") + .containsEntry(AttributeKey.stringKey("3"), "b3") + .containsKeys(AttributeKey.stringKey("service"), AttributeKey.stringKey("host"), AttributeKey.stringKey("host_address")); //ensure that the timings are valid - assertThat(firstSpan.getEndTimestamp()).isLessThan(secondSpan.getEndTimestamp()); - assertThat(secondSpan.getStartTimestamp()).isLessThan(firstSpan.getEndTimestamp()); + assertThat(firstSpan.getEndEpochNanos()).isLessThan(secondSpan.getEndEpochNanos()); + assertThat(secondSpan.getStartEpochNanos()).isLessThan(firstSpan.getEndEpochNanos()); }); } @@ -270,14 +242,11 @@ void testFixedSpanSamplingRate() { samplingTestEndMarker("fixed_end"); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("fixed_end"); - }) - ); + assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("fixed_end"); + })); - long numSpans = exportedSpans.stream().filter(sp -> sp.getName().equals("fixed")).count(); + long numSpans = getExportedSpans().stream().filter(sp -> sp.getName().equals("fixed")).count(); //the number of spans lies with a probability greater than 99.999% +-300 around the mean of 0.5 * 10000 assertThat(numSpans).isGreaterThan(4700).isLessThan(5300); } @@ -291,15 +260,12 @@ void dynamicSampleRate_low() { samplingTestEndMarker("dynamic_end"); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("dynamic_end"); - }) - ); + assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("dynamic_end"); + })); //the number of spans lies with a probability greater than 99.999% +-300 around the mean of 0.2 * 10000 - long numSpans02 = exportedSpans.stream().filter(sp -> sp.getName().equals("dynamic_0.2")).count(); + long numSpans02 = getExportedSpans().stream().filter(sp -> sp.getName().equals("dynamic_0.2")).count(); assertThat(numSpans02).isGreaterThan(1700).isLessThan(2300); } @@ -312,15 +278,12 @@ void dynamicSampleRate_high() { samplingTestEndMarker("dynamic_end"); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("dynamic_end"); - }) - ); + assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("dynamic_end"); + })); //the number of spans lies with a probability greater than 99.999% +-300 around the mean of 0.7 * 10000 - long numSpans07 = exportedSpans.stream().filter(sp -> sp.getName().equals("dynamic_0.7")).count(); + long numSpans07 = getExportedSpans().stream().filter(sp -> sp.getName().equals("dynamic_0.7")).count(); assertThat(numSpans07).isGreaterThan(6700).isLessThan(7300); } @@ -333,15 +296,12 @@ void dynamicSampleRate_invalidRate() { samplingTestEndMarker("dynamic_end"); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("dynamic_end"); - }) - ); + assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("dynamic_end"); + })); //ensure that an invalid probability is equal to "never sample" - long numSpansInvalid = exportedSpans.stream().filter(sp -> sp.getName().equals("invalid")).count(); + long numSpansInvalid = getExportedSpans().stream().filter(sp -> sp.getName().equals("invalid")).count(); assertThat(numSpansInvalid).isEqualTo(10000L); } @@ -354,15 +314,12 @@ void dynamicSampleRate_null() { samplingTestEndMarker("dynamic_end"); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("dynamic_end"); - }) - ); + assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("dynamic_end"); + })); //ensure that an invalid probability is equal to "never sample" - long numSpansNull = exportedSpans.stream().filter(sp -> sp.getName().equals("null")).count(); + long numSpansNull = getExportedSpans().stream().filter(sp -> sp.getName().equals("null")).count(); assertThat(numSpansNull).isEqualTo(10000L); } @@ -375,28 +332,20 @@ void testNestedZeroSamplingProbability() { samplingTestEndMarker("nested_zero_end"); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("nested_zero_end"); - }) - ); - - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(3) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestRoot"); - assertThat(sp.getParentSpanId()).isNull(); - }) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestNested"); - assertThat(sp.getParentSpanId()).isNotNull(); - }) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestNestedDefault"); - assertThat(sp.getParentSpanId()).isNotNull(); - }) + assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("nested_zero_end"); + })); + + assertTraceExported((spans) -> assertThat(spans).hasSize(3).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestRoot"); + assertThat(SpanId.isValid(sp.getParentSpanId())).isFalse(); + }).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestNested"); + assertThat(SpanId.isValid(sp.getParentSpanId())).isTrue(); + }).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestNestedDefault"); + assertThat(SpanId.isValid(sp.getParentSpanId())).isTrue(); + }) ); } @@ -410,15 +359,11 @@ void testNestedOneSamplingProbability() { samplingTestEndMarker("nested_one_end"); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("nested_one_end"); - }) - ); + assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("nested_one_end"); + })); - assertThat(exportedSpans) - .noneSatisfy(sp -> assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestRoot")) + assertThat(getExportedSpans()).noneSatisfy(sp -> assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestRoot")) .noneSatisfy(sp -> assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestNestedDefault")) .anySatisfy(sp -> assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestNested")); } @@ -432,15 +377,11 @@ void testNestedNullSamplingProbability() { samplingTestEndMarker("nested_null_end"); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("nested_null_end"); - }) - ); + assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("nested_null_end"); + })); - assertThat(exportedSpans) - .noneSatisfy(sp -> assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestRoot")) + assertThat(getExportedSpans()).noneSatisfy(sp -> assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestRoot")) .noneSatisfy(sp -> assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestNestedDefault")) .noneSatisfy(sp -> assertThat(sp.getName()).isEqualTo("TraceSettingsTest.nestedSamplingTestNested")); } @@ -466,14 +407,10 @@ void testWithoutErrorStatus() { withoutErrorStatus(); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(1) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withoutErrorStatus"); - assertThat(sp.getStatus()).isEqualTo(Status.OK); - }) - ); + assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withoutErrorStatus"); + assertThat(sp.getStatus()).isEqualTo(Status.OK); + })); } @Test @@ -481,14 +418,10 @@ void testNullErrorStatus() { withErrorStatus(null); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(1) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withErrorStatus"); - assertThat(sp.getStatus()).isEqualTo(Status.OK); - }) - ); + assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withErrorStatus"); + assertThat(sp.getStatus()).isEqualTo(Status.OK); + })); } @Test @@ -496,14 +429,10 @@ void testFalseErrorStatus() { withErrorStatus(false); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(1) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withErrorStatus"); - assertThat(sp.getStatus()).isEqualTo(Status.OK); - }) - ); + assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withErrorStatus"); + assertThat(sp.getStatus()).isEqualTo(Status.OK); + })); } @Test @@ -511,14 +440,10 @@ void testNonNullErrorStatus() { withErrorStatus("foo"); //wait for the end marker, this ensures that all sampled spans are also exported - assertTraceExported((spans) -> - assertThat(spans) - .hasSize(1) - .anySatisfy((sp) -> { - assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withErrorStatus"); - assertThat(sp.getStatus()).isEqualTo(Status.UNKNOWN); - }) - ); + assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { + assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withErrorStatus"); + assertThat(sp.getStatus()).isEqualTo(Status.UNKNOWN); + })); } } diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceTestBase.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceTestBase.java index 9906890caa..38fb5f2fa7 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceTestBase.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceTestBase.java @@ -1,17 +1,15 @@ package rocks.inspectit.ocelot.instrumentation.tracing; -import io.opencensus.trace.TraceId; -import io.opencensus.trace.Tracing; -import io.opencensus.trace.export.SpanData; -import io.opencensus.trace.export.SpanExporter; +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.data.SpanData; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import rocks.inspectit.ocelot.instrumentation.InstrumentationSysTestBase; +import rocks.inspectit.ocelot.utils.TestUtils; import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -21,38 +19,37 @@ public class TraceTestBase extends InstrumentationSysTestBase { - protected Collection exportedSpans = new ConcurrentLinkedQueue<>(); + final static InMemorySpanExporter spanExporter = TestUtils.initializeOpenTelemetryForSystemTesting(); + + protected List getExportedSpans() { + return spanExporter.getFinishedSpanItems(); + } @BeforeEach void setupExporter() { - exportedSpans.clear(); - Tracing.getExportComponent().getSpanExporter().registerHandler("mock", new SpanExporter.Handler() { - @Override - public void export(Collection spanDataList) { - exportedSpans.addAll(spanDataList); - } - }); + spanExporter.reset(); } @AfterEach void destroyExporter() { - exportedSpans.clear(); - Tracing.getExportComponent().getSpanExporter().unregisterHandler("mock"); + spanExporter.reset(); } void assertTraceExported(Consumer> assertions) { await().atMost(15, TimeUnit.SECONDS).untilAsserted(() -> { - Map> traces = exportedSpans.stream() - .collect(Collectors.groupingBy(s -> s.getContext().getTraceId(), Collectors.toList())); + Map> traces = getExportedSpans().stream() + .collect(Collectors.groupingBy(s -> s.getSpanContext().getTraceId(), Collectors.toList())); assertThat(traces.values()).anySatisfy(assertions); assertThat(traces.values()).filteredOnAssertions(assertions).hasSize(1); }); } - void assertSpansExported(Consumer> assertions) { + void assertSpansExported(Consumer> assertions) { + await().atMost(15, TimeUnit.SECONDS).untilAsserted(() -> { - assertions.accept(exportedSpans); + assertions.accept(getExportedSpans()); }); } + } diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java index 18fa4545a4..4b05346e2c 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java @@ -1,6 +1,7 @@ package rocks.inspectit.ocelot.utils; import com.google.common.cache.Cache; +import io.opencensus.impl.internal.DisruptorEventQueue; import io.opencensus.metrics.LabelKey; import io.opencensus.metrics.LabelValue; import io.opencensus.metrics.Metrics; @@ -10,11 +11,29 @@ import io.opencensus.tags.TagKey; import io.opencensus.tags.TagValue; import io.opencensus.tags.Tags; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.extension.trace.propagation.JaegerPropagator; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import org.awaitility.core.ConditionTimeoutException; import rocks.inspectit.ocelot.bootstrap.AgentManager; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; @@ -184,7 +203,7 @@ public static void waitForClassHooks(List> clazzes, int duration, TimeU public static void waitForOpenCensusQueueToBeProcessed() { CountDownLatch latch = new CountDownLatch(1); // TODO: re-implement with OTel - // DisruptorEventQueue.getInstance().enqueue(latch::countDown); + DisruptorEventQueue.getInstance().enqueue(latch::countDown); try { latch.await(3, TimeUnit.SECONDS); } catch (InterruptedException e) { @@ -286,6 +305,55 @@ public static TimeSeries getTimeseries(String metricName, Map ta return series.get(); } + /** + * Initialize {@link io.opentelemetry.api.OpenTelemetry} with a {@link InMemorySpanExporter} so that we can access the exported {@link io.opentelemetry.api.trace.Span}s + * + * @return The {@link InMemorySpanExporter} that can be used to retrieve exported {@link io.opentelemetry.api.trace.Span}s + */ + public static InMemorySpanExporter initializeOpenTelemetryForSystemTesting() { + + + System.out.println("initialize"); + // create an SdkTracerProvider with InMemorySpanExporter and LoggingSpanExporter + InMemorySpanExporter spanExporter = InMemorySpanExporter.create(); + + SdkTracerProvider tracerProvider = SdkTracerProvider.builder() + .setSampler(Sampler.alwaysOn()) + .addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build()) + .addSpanProcessor(SimpleSpanProcessor.create(new LoggingSpanExporter())) + .setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "rocks.inspectit.ocelot.system.test"))) + .build(); + + GlobalOpenTelemetry.resetForTest(); + + OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance()))) + .buildAndRegisterGlobal(); + + // set the OTEL_TRACER in OpenTelemetrySpanBuilderImpl via reflection. + // this needs to be done in case that another code already registered OTEL and the OTEL_TRACER is pointing to the wrong Tracer + try { + Field tracerField = null; + Tracer tracer = GlobalOpenTelemetry.getTracer("io.opentelemetry.opencensusshim"); + tracerField = Class.forName("io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl") + .getDeclaredField("OTEL_TRACER"); + // set static final field + tracerField.setAccessible(true); + Field modifiers = Field.class.getDeclaredField("modifiers"); + modifiers.setAccessible(true); + modifiers.setInt(tracerField, tracerField.getModifiers() & ~Modifier.FINAL); + tracerField.set(null, tracer); + + System.out.println("OTEL_TRACER updated to " + tracer + " (" + openTelemetry.getTracer("io.opentelemetry.opencensusshim") + ")"); + } catch (Exception e) { + e.printStackTrace(); + System.err.println("Failed to set OTEL_TRACER in OpenTelemetrySpanBuilderImpl"); + } + + return spanExporter; + } + private static long getInstrumentationQueueLength() { ViewManager viewManager = Stats.getViewManager(); AggregationData.LastValueDataLong queueSize = (AggregationData.LastValueDataLong) viewManager.getView(View.Name.create("inspectit/self/instrumentation-queue-size")) diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/InspectitConfig.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/InspectitConfig.java index e8a738a87f..6375714e82 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/InspectitConfig.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/InspectitConfig.java @@ -114,10 +114,10 @@ public class InspectitConfig { /** * Can only be specified as JVM-property. - * If true, OpenCensus will be loaded to the bootstrap and accessible by the target application. + * If true, OpenTelemetry will be loaded to the bootstrap and accessible by the target application. */ @UISettings(exclude = true) - private boolean publishOpenCensusToBootstrap; + private boolean publishOpenTelemetryToBootstrap; /** * Environment information. diff --git a/inspectit-ocelot-core/build.gradle b/inspectit-ocelot-core/build.gradle index f2193bca19..d20cf05e59 100644 --- a/inspectit-ocelot-core/build.gradle +++ b/inspectit-ocelot-core/build.gradle @@ -102,6 +102,8 @@ dependencies { "io.opentelemetry:opentelemetry-sdk", "io.opentelemetry:opentelemetry-opencensus-shim", "io.opentelemetry:opentelemetry-semconv", + "io.opentelemetry:opentelemetry-sdk-testing", + 'org.springframework:spring-test:5.1.3.RELEASE', // to make use of SpyBean "org.springframework.boot:spring-boot-test:2.1.4.RELEASE", diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/StackTraceSampler.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/StackTraceSampler.java index 49c5dc157c..0934ecb522 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/StackTraceSampler.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/StackTraceSampler.java @@ -30,7 +30,7 @@ public class StackTraceSampler { /** * Stack-trace samples require a post-processing before being exported as traces. - * This happens asynchronously to the application to minimize the performance impact in a periodicalyl executed task. + * This happens asynchronously to the application to minimize the performance impact in a periodically executed task. * This value defines the frequency at which the queue is checked. */ public static final int EXPORT_INTERVAL_MILLIS = 200; @@ -144,7 +144,7 @@ public AutoCloseable createAndEnterSpan(String name, SpanContext remoteParent, S } /** - * Continues a span but makes it's children stack-trace sampling aware. + * Continues a span but makes its children stack-trace sampling aware. *

* If mode is ENABLE and stack trace sampling is not active for the current, * this span will generate its children using stack trace sampling. diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/correlation/log/LogTraceCorrelatorImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/correlation/log/LogTraceCorrelatorImpl.java index d8f87d4a70..5c05d43c4b 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/correlation/log/LogTraceCorrelatorImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/correlation/log/LogTraceCorrelatorImpl.java @@ -2,7 +2,6 @@ import io.opencensus.trace.SpanContext; import io.opencensus.trace.TraceId; -import io.opencensus.trace.Tracer; import io.opencensus.trace.Tracing; import lombok.AllArgsConstructor; import lombok.Setter; @@ -32,13 +31,11 @@ public class LogTraceCorrelatorImpl implements LogTraceCorrelator { @Setter private String traceIdKey; - private final Tracer tracer = Tracing.getTracer(); - @Override public AutoCloseable startCorrelatedSpanScope(Supplier spanScopeStarter) { - TraceId oldId = tracer.getCurrentSpan().getContext().getTraceId(); + TraceId oldId = Tracing.getTracer().getCurrentSpan().getContext().getTraceId(); AutoCloseable spanScope = spanScopeStarter.get(); - SpanContext newContext = tracer.getCurrentSpan().getContext(); + SpanContext newContext = Tracing.getTracer().getCurrentSpan().getContext(); TraceId newId = newContext.getTraceId(); if (oldId.equals(newId) || !newContext.isValid() || !newContext.getTraceOptions().isSampled()) { return spanScope; @@ -55,6 +52,7 @@ public AutoCloseable startCorrelatedSpanScope(Supplier * Injects the given span context into all configured MDCs. * * @param context the context to inject + * * @return an {@link InjectionScope} to revert the injection and restore the initial state of the MDC */ private InjectionScope injectTraceContextInMdc(SpanContext context) { @@ -86,7 +84,7 @@ public Callable wrap(Callable callable) { @Override public InjectionScope injectTraceIdIntoMdc() { - return injectTraceContextInMdc(tracer.getCurrentSpan().getContext()); + return injectTraceContextInMdc(Tracing.getTracer().getCurrentSpan().getContext()); } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/ContinueOrStartSpanAction.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/ContinueOrStartSpanAction.java index 49ab2401b9..2a80516ca6 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/ContinueOrStartSpanAction.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/actions/span/ContinueOrStartSpanAction.java @@ -97,9 +97,7 @@ private boolean continueSpan(ExecutionContext context) { Object spanObj = ctx.getData(continueSpanDataKey); if (spanObj instanceof Span) { MethodReflectionInformation methodInfo = context.getHook().getMethodInformation(); - AutoCloseable spanCtx = Instances.logTraceCorrelator.startCorrelatedSpanScope(() -> - stackTraceSampler.continueSpan((Span) spanObj, methodInfo, autoTrace) - ); + AutoCloseable spanCtx = Instances.logTraceCorrelator.startCorrelatedSpanScope(() -> stackTraceSampler.continueSpan((Span) spanObj, methodInfo, autoTrace)); ctx.setSpanScope(spanCtx); return true; } @@ -120,13 +118,13 @@ private void startSpan(ExecutionContext context) { boolean hasLocalParent = false; if (remoteParent == null) { Span currentSpan = Tracing.getTracer().getCurrentSpan(); - hasLocalParent = currentSpan != BlankSpan.INSTANCE; + + // the span has a local parent if the currentSpan is either not BlankSpan.INSTANCE (when using OC) or does not have a valid context (when using OTel) + hasLocalParent = !(currentSpan == BlankSpan.INSTANCE || !currentSpan.getContext().isValid()); } Sampler sampler = getSampler(context); - AutoCloseable spanCtx = Instances.logTraceCorrelator.startCorrelatedSpanScope(() -> - stackTraceSampler.createAndEnterSpan(spanName, remoteParent, sampler, spanKind, methodInfo, autoTrace) - ); + AutoCloseable spanCtx = Instances.logTraceCorrelator.startCorrelatedSpanScope(() -> stackTraceSampler.createAndEnterSpan(spanName, remoteParent, sampler, spanKind, methodInfo, autoTrace)); ctx.setSpanScope(spanCtx); commonTagsToAttributesManager.writeCommonTags(Tracing.getTracer() .getCurrentSpan(), remoteParent != null, hasLocalParent); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java similarity index 98% rename from inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceTest.java rename to inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java index 67938c4ca2..2e062a8a68 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java @@ -24,7 +24,7 @@ import static org.assertj.core.api.Assertions.assertThat; -public class LoggingMetricsExporterServiceTest extends SpringTestBase { +public class LoggingMetricsExporterServiceIntTest extends SpringTestBase { @RegisterExtension LogCapturer metricLogs = LogCapturer.create().captureForType(LoggingMetricExporter.class); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java similarity index 96% rename from inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceTest.java rename to inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java index d2dc2b4610..7a8d811176 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java @@ -21,7 +21,7 @@ import static org.assertj.core.api.Assertions.assertThat; -public class LoggingTraceExporterServiceTest extends SpringTestBase { +public class LoggingTraceExporterServiceIntTest extends SpringTestBase { public static final String INSTRUMENTATION_NAME = "rocks.inspectit.ocelot.instrumentation"; @@ -186,12 +186,12 @@ void verifyOpenCensusTraceSent() throws InterruptedException { // turn off tracing exporter localSwitch(false); // make sure no more spans are recorded - Thread.sleep(500); + Awaitility.await().untilAsserted(() -> assertThat(service.isEnabled()).isFalse()); assertThat(spanLogs.size()).isEqualTo(numEvents); // turn tracing exporter back on localSwitch(true); - Thread.sleep(500); + Awaitility.await().untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); // make spans makeSpans(); From f6d894497582114dc72982df44041d90c53b1804 Mon Sep 17 00:00:00 2001 From: Marius Oehler Date: Thu, 23 Dec 2021 11:05:42 +0100 Subject: [PATCH 20/86] Small refactoring and cleanup --- ...java => ActionMetricsRecorderSysTest.java} | 2 +- .../ocelot/config/default/self-monitoring.yml | 7 ++- .../core/instrumentation/hook/MethodHook.java | 6 +- .../hook/MethodHookGenerator.java | 1 - .../selfmonitoring/ActionMetricsRecorder.java | 63 +++---------------- .../selfmonitoring/ActionScopeFactory.java | 11 ++-- .../core/selfmonitoring/ActionScopeImpl.java | 34 +++------- .../core/selfmonitoring/IActionScope.java | 17 +---- .../core/selfmonitoring/NoopActionScope.java | 28 --------- .../instrumentation/hook/MethodHookTest.java | 20 ++++++ .../ActionMetricsRecorderConfigTest.java | 5 +- .../ActionScopeFactoryTest.java | 48 ++++++++++++++ .../selfmonitoring/ActionScopeImplTest.java | 39 ++++++++++++ 13 files changed, 139 insertions(+), 142 deletions(-) rename inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/{ActionMetricsRecorderTestAgent.java => ActionMetricsRecorderSysTest.java} (97%) delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/NoopActionScope.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactoryTest.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImplTest.java diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderTestAgent.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderSysTest.java similarity index 97% rename from inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderTestAgent.java rename to inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderSysTest.java index bcebd8521d..3c75120a79 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderTestAgent.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderSysTest.java @@ -16,7 +16,7 @@ /** * Tests the {@link rocks.inspectit.ocelot.core.selfmonitoring.ActionsMetricsRecorder} */ -public class ActionMetricsRecorderTestAgent extends MetricsSysTestBase { +public class ActionMetricsRecorderSysTest extends MetricsSysTestBase { private static final ViewManager viewManager = Stats.getViewManager(); diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml index bb7fb339bd..76782c39e1 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml @@ -1,14 +1,15 @@ inspectit: - # when enabled inspectit reports execution times for metric and trace collection and processing + # when enabled inspectIT reports execution times for metric and trace collection and processing self-monitoring: enabled: true # settings regarding the capturing of action related metrics - actionMetrics: + action-metrics: # if true, the execution time (duration in ms) per action will be recorded enabled: true + # definitions of existing self-monitoring metrics metrics: definitions: '[inspectit/self/duration]': @@ -43,7 +44,7 @@ inspectit: tags: {"level": true} '[inspectit/self/action/execution-time]': - enabled: ${inspectit.self-monitoring.actionMetrics.enabled} + enabled: ${inspectit.self-monitoring.action-metrics.enabled} type: LONG unit: us description: "the execution time of the action" diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java index 4dd0be1cc7..a8173c0318 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java @@ -10,7 +10,6 @@ import rocks.inspectit.ocelot.core.instrumentation.context.ContextManager; import rocks.inspectit.ocelot.core.instrumentation.context.InspectitContextImpl; import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; -import rocks.inspectit.ocelot.core.selfmonitoring.ActionMetricsRecorder; import rocks.inspectit.ocelot.core.selfmonitoring.ActionScopeFactory; import rocks.inspectit.ocelot.core.selfmonitoring.IActionScope; @@ -70,7 +69,6 @@ public class MethodHook implements IMethodHook { /** * The factory that creates/spawns new scopes for an {@link IHookAction} */ - @NotNull private final ActionScopeFactory actionScopeFactory; @Builder @@ -94,7 +92,7 @@ public InternalInspectitContext onEnter(Object[] args, Object thiz) { IHookAction.ExecutionContext executionContext = new IHookAction.ExecutionContext(args, thiz, null, null, this, inspectitContext); for (IHookAction action : activeEntryActions) { - try (IActionScope scope = actionScopeFactory.getScope(action)) { + try (IActionScope scope = actionScopeFactory.createScope(action)) { action.execute(executionContext); } catch (Throwable t) { log.error("Entry action {} executed for method {} threw an exception and from now on is disabled!", action, methodInformation.getMethodFQN(), t); @@ -110,7 +108,7 @@ public InternalInspectitContext onEnter(Object[] args, Object thiz) { public void onExit(Object[] args, Object thiz, Object returnValue, Throwable thrown, InternalInspectitContext context) { IHookAction.ExecutionContext executionContext = new IHookAction.ExecutionContext(args, thiz, returnValue, thrown, this, (InspectitContextImpl) context); for (IHookAction action : activeExitActions) { - try (IActionScope scope = actionScopeFactory.getScope(action)) { + try (IActionScope scope = actionScopeFactory.createScope(action)) { action.execute(executionContext); } catch (Throwable t) { log.error("Exit action {} executed for method {} threw an exception and from now on is disabled!", action, methodInformation.getMethodFQN(), t); diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java index 0bd80c9b2c..0d35f55500 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookGenerator.java @@ -23,7 +23,6 @@ import rocks.inspectit.ocelot.core.instrumentation.hook.tags.CommonTagsToAttributesManager; import rocks.inspectit.ocelot.core.metrics.MeasuresAndViewsManager; import rocks.inspectit.ocelot.core.privacy.obfuscation.ObfuscationManager; -import rocks.inspectit.ocelot.core.selfmonitoring.ActionMetricsRecorder; import rocks.inspectit.ocelot.core.selfmonitoring.ActionScopeFactory; import rocks.inspectit.ocelot.core.tags.CommonTagsManager; diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorder.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorder.java index c86cbe5f49..c3b7ec0ca4 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorder.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorder.java @@ -32,6 +32,11 @@ public class ActionMetricsRecorder extends DynamicallyActivatableService { */ private static final String EXECUTION_TIME_METRIC_NAME = "execution-time"; + /** + * The measurement name used for recording. + */ + private static final String MEASUREMENT_NAME = METRIC_NAME_PREFIX + EXECUTION_TIME_METRIC_NAME; + /** * The key of the action's name used in custom tags. */ @@ -41,27 +46,13 @@ public ActionMetricsRecorder() { super("metrics.enabled", "selfMonitoring.actionMetrics"); } - /** - * Records the execution time of an {@link IHookAction}. - * - * @param action The action - * @param executionTimeMicros The execution time in microseconds - * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? - */ - public void record(IHookAction action, long executionTimeMicros) { - record(action.getName(), executionTimeMicros); - } - /** * Records the execution time of an action. * * @param actionName The name of the execution * @param executionTimeMicros The execution time in microseconds - * TODO: do we want to record nano, micro or milliseconds? In case of millis, do we want to store long or double? */ public void record(String actionName, long executionTimeMicros) { - - // do nothing if this metrics recorder is not enabled if (!isEnabled()) { return; } @@ -71,47 +62,8 @@ public void record(String actionName, long executionTimeMicros) { put(ACTION_NAME_KEY, actionName); }}; - // record the action's execution time if enabled - recordMeasurement(EXECUTION_TIME_METRIC_NAME, executionTimeMicros, customTags); - - // if we later have different metrics that can be individually turned on or off, we need to check via ActionMetricsSettings what to record, e.g., - - // ActionMetricsSettings actionsSettings = env.getCurrentConfig().getSelfMonitoring().getActionMetrics(); - // if (actionsSettings.isEnabled()) { - // recordMeasurement(EXECUTION_TIME_METRIC_NAME, executionTimeMicros, customTags); - // } - - } - - /** - * Records the measurement for the specific metric of an action. - * - * @param actionName The name of the action - * @param metricName The name of the metric - * @param value The value to be recorded - */ - private void recordMeasurement(String actionName, String metricName, long value) { - // return if the recorder or the metric is disabled - if (!isEnabled()) { - return; - } - // create custom tags - val customTags = new HashMap() {{ - put(ACTION_NAME_KEY, actionName); - }}; - // record measurement - selfMonitoringService.recordMeasurement(METRIC_NAME_PREFIX + metricName, value, customTags); - } - - /** - * Records the measurement for the metric. - * - * @param metricName The name of the metric - * @param value The value to be recorded - * @param customTags additional tags, which are added to the measurement - */ - private void recordMeasurement(String metricName, long value, HashMap customTags) { - selfMonitoringService.recordMeasurement(METRIC_NAME_PREFIX + metricName, value, customTags); + // record the action's execution time + selfMonitoringService.recordMeasurement(MEASUREMENT_NAME, executionTimeMicros, customTags); } @Override @@ -123,7 +75,6 @@ protected boolean checkEnabledForConfig(InspectitConfig configuration) { @Override protected boolean doEnable(InspectitConfig configuration) { - // TODO: do I need to check whether the InspectitConfig#action settings have been defined at all? log.info("Enabling ActionMetricsRecorder."); return true; } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactory.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactory.java index 525e7b8f4e..27078aedef 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactory.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactory.java @@ -13,17 +13,18 @@ public class ActionScopeFactory { @Autowired - ActionMetricsRecorder recorder; + private ActionMetricsRecorder recorder; /** - * Creates and returns a new {@link IActionScope} for the given {@link IHookAction}. If the {@link ActionMetricsRecorder} is disabled, the {@link NoopActionScope} will be returned. + * Creates and returns a new {@link IActionScope} for the given {@link IHookAction}. + * If the {@link ActionMetricsRecorder} is disabled, the {@link IActionScope#NOOP_ACTION_SCOPE} will be returned. * * @param action The action * - * @return A new {@link IActionScope} for the given {@link IHookAction}. A {@link NoopActionScope} is returned if the {@link ActionMetricsRecorder} is disabled. + * @return A new {@link IActionScope} for the given {@link IHookAction} or {@link IActionScope#NOOP_ACTION_SCOPE} in case {@link ActionMetricsRecorder} is disabled. */ - public IActionScope getScope(IHookAction action) { - return recorder.isEnabled() ? new ActionScopeImpl(action, recorder) : NoopActionScope.INSTANCE; + public IActionScope createScope(IHookAction action) { + return recorder.isEnabled() ? new ActionScopeImpl(action, recorder) : IActionScope.NOOP_ACTION_SCOPE; } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImpl.java index da73f48e75..bbdf251250 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImpl.java @@ -1,6 +1,7 @@ package rocks.inspectit.ocelot.core.selfmonitoring; import lombok.Data; +import lombok.Value; import lombok.extern.slf4j.Slf4j; import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; @@ -9,24 +10,24 @@ /** * Functional implementation of the {@link IActionScope} */ -@Data +@Value @Slf4j public class ActionScopeImpl implements IActionScope { /** * The {@link IHookAction} of this scope. */ - private IHookAction action; + private final IHookAction action; /** * The start time of the action/scope in nanoseconds. */ - private long startTimeNanos; + private final long startTimeNanos; /** * The recorder used for recording the metrics of this {@link #action}. */ - private ActionMetricsRecorder recorder; + private final ActionMetricsRecorder recorder; /** * Creates and starts a new {@link ActionScopeImpl} for the given {@link IHookAction} @@ -35,38 +36,17 @@ public class ActionScopeImpl implements IActionScope { * @param recorder The {@link ActionMetricsRecorder} */ public ActionScopeImpl(IHookAction action, ActionMetricsRecorder recorder) { - this(action, recorder, true); - } - - /** - * @param action The {@link IHookAction} of this scope - * @param recorder The {@link ActionMetricsRecorder} - * @param autoStart Whether to automatically start the scope, i.e., setting the {@link #startTimeNanos} - */ - public ActionScopeImpl(IHookAction action, ActionMetricsRecorder recorder, boolean autoStart) { this.action = action; this.recorder = recorder; - if (autoStart) { - start(); - } - } - @Override - public void start() { // set start time of the action/scope in nanoseconds startTimeNanos = System.nanoTime(); } - @Override - public void start(long startTimeNanos) { - this.startTimeNanos = startTimeNanos; - } - @Override public void close() { // record the action's metrics. - long executionTimeMicros = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - startTimeNanos); - // log.info("ActionScopeImpl.close. action={}, startTime={}, endTime={}, executionTimeMillis={}", action.getName(), startTimeNanos, System.nanoTime(), executionTimeMillis); - recorder.record(action, executionTimeMicros); + long executionTimeMicros = (long) ((System.nanoTime() - startTimeNanos) / 1000D); + recorder.record(action.getName(), executionTimeMicros); } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/IActionScope.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/IActionScope.java index 498819d43e..fe4681275d 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/IActionScope.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/IActionScope.java @@ -1,24 +1,13 @@ package rocks.inspectit.ocelot.core.selfmonitoring; -import io.opencensus.common.Scope; import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; /** * Scope for an {@link IHookAction} executed in the {@link rocks.inspectit.ocelot.core.instrumentation.hook.MethodHook} */ -public interface IActionScope extends Scope { - - /** - * Starts the scope. - */ - public abstract void start(); - - /** - * Starts the scope and stores the given start time of the action - * - * @param startTimeNanos The start time of the action in nanoseconds. - */ - public abstract void start(long startTimeNanos); +public interface IActionScope extends AutoCloseable { + IActionScope NOOP_ACTION_SCOPE = () -> { + }; } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/NoopActionScope.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/NoopActionScope.java deleted file mode 100644 index cad5f6b628..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/selfmonitoring/NoopActionScope.java +++ /dev/null @@ -1,28 +0,0 @@ -package rocks.inspectit.ocelot.core.selfmonitoring; - -/** - * Dummy no-op {@link IActionScope} - */ -public class NoopActionScope implements IActionScope { - - public static final NoopActionScope INSTANCE = new NoopActionScope(); - - private NoopActionScope(){ - - } - - @Override - public void start() { - - } - - @Override - public void start(long startTimeNanos) { - - } - - @Override - public void close() { - - } -} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookTest.java index 206be4b117..b5402e98d3 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHookTest.java @@ -64,6 +64,8 @@ void testExceptionHandling() { verify(first, times(1)).execute(any()); verify(second, times(1)).execute(any()); verify(third, times(1)).execute(any()); + verify(actionScopeFactory, times(3)).createScope(any()); + verifyNoMoreInteractions(actionScopeFactory, first, second, third); hook.onExit(null, null, null, null, ctx); verify(context, times(1)).close(); @@ -73,6 +75,8 @@ void testExceptionHandling() { verify(first, times(2)).execute(any()); verify(second, times(1)).execute(any()); verify(third, times(2)).execute(any()); + verify(actionScopeFactory, times(5)).createScope(any()); + verifyNoMoreInteractions(actionScopeFactory, first, second, third); hook.onExit(null, null, null, null, ctx); } @@ -93,11 +97,15 @@ void testReactivationOfActionsOnCopy() { hook.onExit(null, null, null, null, ctx); verify(action, times(1)).execute(any()); + verify(actionScopeFactory).createScope(action); + verifyNoMoreInteractions(actionScopeFactory, action); ctx = hook.onEnter(null, null); hook.onExit(null, null, null, null, ctx); verify(action, times(1)).execute(any()); + verify(actionScopeFactory).createScope(action); + verifyNoMoreInteractions(actionScopeFactory, action); MethodHook copy = hook.getResettedCopy(); @@ -105,6 +113,8 @@ void testReactivationOfActionsOnCopy() { copy.onExit(null, null, null, null, ctx); verify(action, times(2)).execute(any()); + verify(actionScopeFactory, times(2)).createScope(action); + verifyNoMoreInteractions(actionScopeFactory, action); } } @@ -132,6 +142,8 @@ void testExceptionHandling() { verify(first, times(1)).execute(any()); verify(second, times(1)).execute(any()); verify(third, times(1)).execute(any()); + verify(actionScopeFactory, times(3)).createScope(any()); + verifyNoMoreInteractions(actionScopeFactory, first, second, third); ctx = hook.onEnter(null, null); hook.onExit(null, null, null, null, ctx); @@ -139,6 +151,8 @@ void testExceptionHandling() { verify(first, times(2)).execute(any()); verify(second, times(1)).execute(any()); verify(third, times(2)).execute(any()); + verify(actionScopeFactory, times(5)).createScope(any()); + verifyNoMoreInteractions(actionScopeFactory, first, second, third); } @Test @@ -157,11 +171,15 @@ void testReactivationOfActionsOnCopy() { hook.onExit(null, null, null, null, ctx); verify(action, times(1)).execute(any()); + verify(actionScopeFactory).createScope(action); + verifyNoMoreInteractions(actionScopeFactory, action); ctx = hook.onEnter(null, null); hook.onExit(null, null, null, null, ctx); verify(action, times(1)).execute(any()); + verify(actionScopeFactory).createScope(action); + verifyNoMoreInteractions(actionScopeFactory, action); MethodHook copy = hook.getResettedCopy(); @@ -169,6 +187,8 @@ void testReactivationOfActionsOnCopy() { copy.onExit(null, null, null, null, ctx); verify(action, times(2)).execute(any()); + verify(actionScopeFactory, times(2)).createScope(action); + verifyNoMoreInteractions(actionScopeFactory, action); } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java index 22cf056286..70e95115a1 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java @@ -33,7 +33,7 @@ class CheckConfigChanges extends SpringTestBase { void checkAllEnabled() { assertThat(recorder.isEnabled()).isTrue(); updateProperties((mp) -> { - mp.setProperty("inspectit.selfMonitoring.actionMetrics.enabled", "true"); + mp.setProperty("inspectit.selfMonitoring.action-metrics.enabled", "true"); }); assertThat(recorder.isEnabled()).isTrue(); } @@ -43,10 +43,9 @@ void checkAllEnabled() { void checkAllDisabled() { assertThat(recorder.isEnabled()).isTrue(); updateProperties((mp) -> { - mp.setProperty("inspectit.selfMonitoring.actionMetrics.enabled", "false"); + mp.setProperty("inspectit.selfMonitoring.action-metrics.enabled", "false"); }); assertThat(recorder.isEnabled()).isFalse(); } } - } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactoryTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactoryTest.java new file mode 100644 index 0000000000..7aef08e500 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeFactoryTest.java @@ -0,0 +1,48 @@ +package rocks.inspectit.ocelot.core.selfmonitoring; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class ActionScopeFactoryTest { + + @InjectMocks + private ActionScopeFactory factory; + + @Mock + private ActionMetricsRecorder recorder; + + @Nested + class CreateScope { + + @Mock + private IHookAction action; + + @Test + public void enabled() { + when(recorder.isEnabled()).thenReturn(true); + + IActionScope scope = factory.createScope(action); + + assertThat(scope).isInstanceOf(ActionScopeImpl.class); + } + + @Test + public void disabled() { + when(recorder.isEnabled()).thenReturn(false); + + IActionScope scope = factory.createScope(action); + + assertThat(scope).isSameAs(IActionScope.NOOP_ACTION_SCOPE); + } + + } +} \ No newline at end of file diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImplTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImplTest.java new file mode 100644 index 0000000000..98ae26dd96 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionScopeImplTest.java @@ -0,0 +1,39 @@ +package rocks.inspectit.ocelot.core.selfmonitoring; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import rocks.inspectit.ocelot.core.instrumentation.hook.actions.IHookAction; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.longThat; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class ActionScopeImplTest { + + @Mock + private ActionMetricsRecorder recorder; + + @Mock + private IHookAction action; + + @Nested + class Close { + + @Test + public void recordDuration() { + when(action.getName()).thenReturn("action-name"); + + ActionScopeImpl scope = new ActionScopeImpl(action, recorder); + scope.close(); + + verify(recorder).record(eq("action-name"), longThat((val) -> val > 0)); + verifyNoMoreInteractions(recorder); + } + + } +} From d03b5c9e4c1002119a47b4bdfcd3fcef03ac63fd Mon Sep 17 00:00:00 2001 From: Marius Oehler Date: Thu, 23 Dec 2021 11:42:22 +0100 Subject: [PATCH 21/86] Updated documentation and default configuration --- .../ocelot/config/default/self-monitoring.yml | 2 +- .../docs/metrics/self-monitoring.md | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml index 76782c39e1..df9153017e 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/self-monitoring.yml @@ -7,7 +7,7 @@ inspectit: # settings regarding the capturing of action related metrics action-metrics: # if true, the execution time (duration in ms) per action will be recorded - enabled: true + enabled: false # definitions of existing self-monitoring metrics metrics: diff --git a/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md b/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md index 1865eeafb5..3bbcda5136 100644 --- a/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md +++ b/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md @@ -18,6 +18,22 @@ The metric is split by the tag containing the component name and also includes a |```inspectit/self/action/execution-time```|us|The execution time of individual actions. The metric contains the tag `action_name`, specifying the name of the instrumented action. |```inspectit/self/action/count```|`action executions`|The number of executions per action. The metric contains the tag `action_name`, specifying the name of the instrumented action. -Self monitoring is enabled by default and can be disabled by setting the `inspectit.self-monitoring.enabled` property to `false`. +Self monitoring is enabled by default (except action metrics) and can be disabled by setting the `inspectit.self-monitoring.enabled` property to `false`. > Not all components responsible for internal management of inspectIT Ocelot are at the moment reporting the time used for internal tasks. Please take the provided numbers only for a basic reference on overhead and don't assume they are 100% correct. In addition the overhead introduced in application classes through instrumentation is currently also not captured. + +### Action Execution Monitoring + +Since version `1.14.0`, the inspectIT Ocelot agent is able to record execution metrics of its actions. +In order to reduce the overhead the agent is producing on the target system, the action execution metrics recording of actions is disabled by default. + +The recording of these metrics can be enabled using the following configuration: + +```yaml +inspectit: + self-monitoring: + action-metrics: + enabled: false +``` + +Note: the action execution metrics are only recorded in case the self-monitoring metrics are enabled. \ No newline at end of file From 3dfcc5b3dc49bbc8fde2f015d11711ad6995c660 Mon Sep 17 00:00:00 2001 From: Marius Oehler Date: Thu, 23 Dec 2021 11:53:36 +0100 Subject: [PATCH 22/86] Fix recorder tests --- .../ActionMetricsRecorderConfigTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java index 70e95115a1..3bff0ae693 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/selfmonitoring/ActionMetricsRecorderConfigTest.java @@ -21,7 +21,7 @@ class Defaults extends SpringTestBase { @Test void checkDefaultEnabled() { - assertThat(recorder.isEnabled()).isTrue(); + assertThat(recorder.isEnabled()).isFalse(); } } @@ -31,9 +31,9 @@ class CheckConfigChanges extends SpringTestBase { @Test @DirtiesContext void checkAllEnabled() { - assertThat(recorder.isEnabled()).isTrue(); + assertThat(recorder.isEnabled()).isFalse(); updateProperties((mp) -> { - mp.setProperty("inspectit.selfMonitoring.action-metrics.enabled", "true"); + mp.setProperty("inspectit.selfMonitoring.actionMetrics.enabled", "true"); }); assertThat(recorder.isEnabled()).isTrue(); } @@ -41,9 +41,9 @@ void checkAllEnabled() { @Test @DirtiesContext void checkAllDisabled() { - assertThat(recorder.isEnabled()).isTrue(); + assertThat(recorder.isEnabled()).isFalse(); updateProperties((mp) -> { - mp.setProperty("inspectit.selfMonitoring.action-metrics.enabled", "false"); + mp.setProperty("inspectit.selfMonitoring.actionMetrics.enabled", "false"); }); assertThat(recorder.isEnabled()).isFalse(); } From 9ea22c15c936ab8049c229cb0025861502e51217 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 7 Jan 2022 08:49:03 +0100 Subject: [PATCH 23/86] Implemented the concurrent support of io.opentelemetry.context.Context and io.grpc.Context in ContextManager and InspectitContextImpl and implemented the helper class ContextUtil. With this approach, we aim at supporting the transition from OpenCensus to OpenTelemetry with the io.opentelemetry.opencensusshim. While OpenTelemetry uses io.opentelemetry.context.Context, OpenCensus uses io.grpc.Context, and some custom features of inspectIT also work with io.grpc.Context (#1246) --- .../context/ContextManager.java | 102 ++++++++++++++--- .../instrumentation/context/ContextUtil.java | 61 ++++++++++ .../context/InspectitContextImpl.java | 43 +++++-- .../context/InspectitContextImplTest.java | 105 +++++++----------- 4 files changed, 223 insertions(+), 88 deletions(-) create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextUtil.java diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextManager.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextManager.java index 730be90382..3465019017 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextManager.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextManager.java @@ -1,14 +1,13 @@ package rocks.inspectit.ocelot.core.instrumentation.context; -import static java.lang.Boolean.TRUE; - import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import io.grpc.Context; import io.opencensus.tags.Tags; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; import lombok.AllArgsConstructor; import lombok.NonNull; -import lombok.Value; +import org.apache.commons.lang3.tuple.Pair; import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.bootstrap.context.ContextTuple; import rocks.inspectit.ocelot.bootstrap.context.IContextManager; @@ -16,8 +15,11 @@ import rocks.inspectit.ocelot.core.instrumentation.config.InstrumentationConfigurationResolver; import rocks.inspectit.ocelot.core.tags.CommonTagsManager; +import javax.validation.constraints.NotNull; import java.util.concurrent.Callable; +import static java.lang.Boolean.TRUE; + /** * This class is based on the ContextStrategyImpl (https://github.com/census-instrumentation/opencensus-java/blob/master/contrib/agent/src/main/java/io/opencensus/contrib/agent/instrumentation/ContextStrategyImpl.java) * class from the opencensus-java repository. @@ -54,22 +56,54 @@ public ContextManager(CommonTagsManager commonTagsManager, InstrumentationConfig @Override public Runnable wrap(Runnable r) { - return Context.current().wrap(r); + // manually build up own wrap method to support io.grpc.Context and io.telemetry.context.Context + + // get current OpenTelemetry and grpc context + Context current = ContextUtil.current(); + io.grpc.Context currentGrpc = ContextUtil.currentGrpc(); + + return () -> { + // attach current grpc context to obtain the previous context + io.grpc.Context previousGrpc = currentGrpc.attach(); + // wrap runnable in OpenTelemetry context + try (Scope ignored = current.makeCurrent()) { + r.run(); + } finally { + // restore the previous grpc context + currentGrpc.detach(previousGrpc); + } + }; } @Override public Callable wrap(Callable callable) { - return Context.current().wrap(callable); + // manually build up own wrap method to support io.grpc.Context and io.telemetry.context.Context + + // get current OpenTelemetry and grpc context + Context current = ContextUtil.current(); + io.grpc.Context currentGrpc = ContextUtil.currentGrpc(); + + return () -> { + // attach current grpc context to obtain the previous context + io.grpc.Context previousGrpc = currentGrpc.attach(); + try (Scope ignored = current.makeCurrent()) { + return callable.call(); + } finally { + // restore the previous grpc context + currentGrpc.detach(previousGrpc); + } + }; } @Override public InspectitContextImpl enterNewContext() { - return InspectitContextImpl.createFromCurrent(commonTagsManager.getCommonTagValueMap(), configProvider.getCurrentConfig().getPropagationMetaData(), IS_OPEN_CENSUS_ON_BOOTSTRAP); + return InspectitContextImpl.createFromCurrent(commonTagsManager.getCommonTagValueMap(), configProvider.getCurrentConfig() + .getPropagationMetaData(), IS_OPEN_CENSUS_ON_BOOTSTRAP); } @Override public void storeContext(Object target, boolean invalidateAfterRestoring) { - InvalidationContext invalidationContext = new InvalidationContext(invalidateAfterRestoring, Context.current()); + InvalidationContext invalidationContext = new InvalidationContext(invalidateAfterRestoring, ContextUtil.current(), ContextUtil.currentGrpc()); contextCache.put(target, invalidationContext); } @@ -81,13 +115,13 @@ public ContextTuple attachContext(Object target) { contextCache.invalidate(target); } // restore/attach context to current runtime/thread - Context previous = invalidationContext.context.attach(); + Pair previous = invalidationContext.attach(); // once the context is attached, we inject the trace id into the MDCs for log-trace correlation AutoCloseable undoTraceInjection = Instances.logTraceCorrelator.injectTraceIdIntoMdc(); // data we need once the method exits in order to undo the previous changes - return new ContextTupleImpl(previous, invalidationContext.context, undoTraceInjection); + return new ContextTupleImpl(previous.getLeft(), invalidationContext.context, previous.getRight(), invalidationContext.contextGrpc, undoTraceInjection); } return null; } @@ -104,7 +138,7 @@ public void detachContext(ContextTuple contextTuple) { } // restore previous context - tuple.current.detach(tuple.previous); + tuple.detach(); } } @@ -137,10 +171,27 @@ private class InvalidationContext { /** * Whether the context should be removed from the context cache after it has been restored. */ - private final boolean invalidate; + private boolean invalidate; + /** + * The {@link io.opentelemetry.context.Context} used by OpenTelemetry + */ @NonNull - private final Context context; + private Context context; + + /** + * The {@link io.grpc.Context} used by OpenCensus + */ + @NotNull + private io.grpc.Context contextGrpc; + + /** + * Attaches the {@link #context} and {@link #contextGrpc} and enters a new scope within the current context. + * @return The {@link Pair} containing the {@link Scope} of {@link Context#makeCurrent()} and the attached {@link io.grpc.Context} of {@link io.grpc.Context#attach()} + */ + private Pair attach() { + return Pair.of(context.makeCurrent(), contextGrpc.attach()); + } } /** @@ -153,7 +204,7 @@ private class ContextTupleImpl implements ContextTuple { * The previous context. */ @NonNull - private final Context previous; + private final Scope previous; /** * The context which has been attached. @@ -161,10 +212,33 @@ private class ContextTupleImpl implements ContextTuple { @NonNull private final Context current; + /** + * The previous {@link io.grpc.Context} used by OpenCensus + */ + @NotNull + private final io.grpc.Context previousGrpc; + + /** + * The {@link io.grpc.Context} used by OpenCensus which has been attached + */ + @NotNull + private final io.grpc.Context currentGrpc; + /** * {@link AutoCloseable} for undoing the trace id injection into the logging MDCs. */ @NonNull private final AutoCloseable undoTraceInjection; + + /** + * Restores the previous {@link #previous} and {@link #previousGrpc} + */ + private void detach() { + // close the OTEL scope to restore the previous OTEL context + previous.close(); + // restore the previous GRPC context + currentGrpc.detach(previousGrpc); + } } + } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextUtil.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextUtil.java new file mode 100644 index 0000000000..c771ac3bc8 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextUtil.java @@ -0,0 +1,61 @@ +package rocks.inspectit.ocelot.core.instrumentation.context; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import rocks.inspectit.ocelot.bootstrap.exposed.InspectitContext; + +/** + * Utility class to get the current OTEL {@link io.opentelemetry.context.Context} and GRPC {@link io.grpc.Context}. + *

+ * This utility class is a response to the change from {@link io.grpc.Context} to {@link io.opentelemetry.context.Context} in OpenTelemetry. + */ +public class ContextUtil { + + /** + * Gets the current OTEL {@link Context} via {@link Context#current()} + * + * @return The current OTEL {@link Context} via {@link Context#current()} + */ + public static io.opentelemetry.context.Context current() { + return io.opentelemetry.context.Context.current(); + } + + /** + * Gets the current GRPC {@link io.grpc.Context} via {@link io.grpc.Context#current()} + * + * @return The current GRPC {@link io.grpc.Context} via {@link io.grpc.Context#current()} + */ + public static io.grpc.Context currentGrpc() { + return io.grpc.Context.current(); + } + + /** + * Gets the {@link InspectitContext} stored in the {@link #current() current OTEL context} with the {@link InspectitContextImpl#INSPECTIT_KEY} + * + * @return The {@link InspectitContext} stored in the {@link #current() current OTEL context} with the {@link InspectitContextImpl#INSPECTIT_KEY} + */ + public static InspectitContextImpl currentInspectitContext() { + return current().get(InspectitContextImpl.INSPECTIT_KEY); + } + + /** + * Gets the {@link InspectitContext} stored in the {@link #currentGrpc() current GRPC context} with the {@link InspectitContextImpl#INSPECTIT_KEY_GRPC} + * + * @return The {@link InspectitContext} stored in the {@link #currentGrpc() current GRPC context} with the {@link InspectitContextImpl#INSPECTIT_KEY_GRPC} + */ + public static InspectitContextImpl currentInspectitContextStoredInGrpcContext() { + return InspectitContextImpl.INSPECTIT_KEY_GRPC.get(currentGrpc()); + } + + /** + * Gets the object stored in {@link #current()} with the given {@link ContextKey} + * + * @param key The {@link ContextKey} under which the object is stored + * @param The type of the object + * + * @return The object stored under {@link #current()} with the given {@link ContextKey} + */ + public static T get(ContextKey key) { + return current().get(key); + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/InspectitContextImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/InspectitContextImpl.java index 2ec33d9b24..89a7d3b33f 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/InspectitContextImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/InspectitContextImpl.java @@ -1,12 +1,14 @@ package rocks.inspectit.ocelot.core.instrumentation.context; -import io.grpc.Context; import io.opencensus.common.Function; import io.opencensus.common.Scope; import io.opencensus.tags.*; +import io.opencensus.trace.ContextHandle; import io.opencensus.trace.Span; import io.opencensus.trace.SpanContext; import io.opencensus.trace.Tracing; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; import lombok.extern.slf4j.Slf4j; import lombok.val; import rocks.inspectit.ocelot.bootstrap.context.InternalInspectitContext; @@ -77,7 +79,9 @@ public class InspectitContextImpl implements InternalInspectitContext { */ private static final Set> ALLOWED_TAG_TYPES = new HashSet<>(Arrays.asList(String.class, Character.class, Long.class, Integer.class, Short.class, Byte.class, Double.class, Float.class, Boolean.class)); - static final Context.Key INSPECTIT_KEY = Context.key("inspectit-context"); + static final io.opentelemetry.context.ContextKey INSPECTIT_KEY = ContextKey.named("inspectit-context"); + + static final io.grpc.Context.Key INSPECTIT_KEY_GRPC = io.grpc.Context.key("inspectit-context"); /** * Points to the parent from which this context inherits its data and to which potential up-propagation is performed. @@ -107,7 +111,12 @@ public class InspectitContextImpl implements InternalInspectitContext { /** * Holds the previous GRPC context which was overridden when attaching this context as active in GRPC. */ - private Context overriddenGrpcContext; + private io.grpc.Context overriddenGrpcContext; + + /** + * Holds the previous {@link ContextHandle} associated with OTELs {@link Context}, which was overridden when attaching this context as active in OTEL + */ + private io.opentelemetry.context.Scope overriddenOtelScope; /** * The span which was (potentially) opened by invoking {@link #enterSpan(Span)} @@ -209,7 +218,8 @@ private InspectitContextImpl(InspectitContextImpl parent, PropagationMetaData de * @return the newly created context */ public static InspectitContextImpl createFromCurrent(Map commonTags, PropagationMetaData defaultPropagation, boolean interactWithApplicationTagContexts) { - InspectitContextImpl parent = INSPECTIT_KEY.get(); + InspectitContextImpl parent = ContextUtil.currentInspectitContext(); + InspectitContextImpl result = new InspectitContextImpl(parent, defaultPropagation, interactWithApplicationTagContexts); if (parent == null) { @@ -263,7 +273,10 @@ public void makeActive() { } cachedActivePhaseDownPropagatedData = postEntryPhaseDownPropagatedData; - overriddenGrpcContext = Context.current().withValue(INSPECTIT_KEY, this).attach(); + // store the Context in OTEL + overriddenOtelScope = ContextUtil.current().with(INSPECTIT_KEY, this).makeCurrent(); + // store the Context in GRPC context + overriddenGrpcContext = ContextUtil.currentGrpc().withValue(INSPECTIT_KEY_GRPC, this).attach(); if (interactWithApplicationTagContexts) { Tagger tagger = Tags.getTagger(); @@ -304,7 +317,7 @@ public Iterable> getData() { * @return true, if {@link #makeActive()} was called but {@link #close()} was not called yet */ public boolean isInActiveOrExitPhase() { - return overriddenGrpcContext != null; + return overriddenOtelScope != null; } /** @@ -377,7 +390,16 @@ public void close() { if (openedDownPropagationScope != null) { openedDownPropagationScope.close(); } - Context.current().detach(overriddenGrpcContext); + + // close the overridden OTEL scope + if (null != overriddenOtelScope) { + overriddenOtelScope.close(); + } + + // detach the overridden GRPC context + if (null != overriddenGrpcContext){ + ContextUtil.currentGrpc().detach(overriddenGrpcContext); + } if (currentSpanScope != null) { try { @@ -394,6 +416,7 @@ public void close() { openedDownPropagationScope = null; currentSpanScope = null; parent = null; + overriddenOtelScope = null; overriddenGrpcContext = null; } @@ -426,14 +449,12 @@ public Map getDownPropagationHeaders() { spanContext = null; } } - return ContextPropagationUtil.buildPropagationHeaderMap(getDataAsStream().filter(e -> propagation.isPropagatedDownGlobally(e - .getKey())), spanContext); + return ContextPropagationUtil.buildPropagationHeaderMap(getDataAsStream().filter(e -> propagation.isPropagatedDownGlobally(e.getKey())), spanContext); } @Override public Map getUpPropagationHeaders() { - return ContextPropagationUtil.buildPropagationHeaderMap(getDataAsStream().filter(e -> propagation.isPropagatedUpGlobally(e - .getKey()))); + return ContextPropagationUtil.buildPropagationHeaderMap(getDataAsStream().filter(e -> propagation.isPropagatedUpGlobally(e.getKey()))); } @Override diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/context/InspectitContextImplTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/context/InspectitContextImplTest.java index b349d8238d..c56ce1837c 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/context/InspectitContextImplTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/context/InspectitContextImplTest.java @@ -1,6 +1,5 @@ package rocks.inspectit.ocelot.core.instrumentation.context; -import io.grpc.Context; import io.opencensus.common.Scope; import io.opencensus.tags.*; import io.opencensus.trace.Span; @@ -18,6 +17,7 @@ import rocks.inspectit.ocelot.bootstrap.context.InternalInspectitContext; import rocks.inspectit.ocelot.bootstrap.correlation.noop.NoopLogTraceCorrelator; import rocks.inspectit.ocelot.config.model.instrumentation.data.PropagationMode; +import rocks.inspectit.ocelot.core.SpringTestBase; import rocks.inspectit.ocelot.core.instrumentation.config.model.propagation.PropagationMetaData; import rocks.inspectit.ocelot.core.testutils.GcUtils; @@ -33,7 +33,7 @@ import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) -public class InspectitContextImplTest { +public class InspectitContextImplTest extends SpringTestBase { @Mock PropagationMetaData propagation; @@ -69,7 +69,6 @@ void verifyCleared() { } - @Nested public class EnterSpan { @@ -79,6 +78,7 @@ public class EnterSpan { @BeforeEach void setup() { Instances.logTraceCorrelator = traceCorrelator; + } @AfterEach @@ -93,7 +93,8 @@ void spanEntered() { InspectitContextImpl ctx = InspectitContextImpl.createFromCurrent(new HashMap<>(), propagation, false); assertThat(ctx.hasEnteredSpan()).isFalse(); - ctx.setSpanScope(Tracing.getTracer().withSpan(mySpan)); + Scope scope = Tracing.getTracer().withSpan(mySpan); + ctx.setSpanScope(scope); ctx.makeActive(); @@ -105,10 +106,8 @@ void spanEntered() { assertThat(Tracing.getTracer().getCurrentSpan()).isNotSameAs(mySpan); } - } - @Nested public class DownPropagation { @@ -125,10 +124,9 @@ void verifyCommonTagsExtracted() { assertThat(ctx.getData("tagB")).isEqualTo("valueB"); ctx.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyCommonTagsPropagatedAndOverwritable() { HashMap tags = new HashMap<>(); @@ -149,10 +147,9 @@ void verifyCommonTagsPropagatedAndOverwritable() { ctxB.close(); ctxA.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyOverwritesAreLocal() { when(propagation.isPropagatedDownWithinJVM(any())).thenReturn(true); @@ -178,7 +175,6 @@ void verifyOverwritesAreLocal() { assertThat(ctxC.getData("keyA")).isEqualTo("ctxA_valueA"); assertThat(ctxC.getData("keyB")).isEqualTo("ctxB_valueB"); - ctxC.close(); ctxB.close(); //ensure no up propagation took place @@ -186,10 +182,9 @@ void verifyOverwritesAreLocal() { assertThat(ctxA.getData("keyB")).isEqualTo("ctxA_valueB"); ctxA.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyOverwritesHappenOnlyWhenConfigured() { doReturn(true).when(propagation).isPropagatedDownWithinJVM(eq("keyA")); @@ -216,7 +211,6 @@ void verifyOverwritesHappenOnlyWhenConfigured() { assertThat(ctxC.getData("keyA")).isEqualTo("ctxA_valueA"); assertThat(ctxC.getData("keyB")).isNull(); - ctxC.close(); ctxB.close(); //ensure no up propagation took place @@ -224,10 +218,9 @@ void verifyOverwritesHappenOnlyWhenConfigured() { assertThat(ctxA.getData("keyB")).isEqualTo("ctxA_valueB"); ctxA.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyContextReleasedWhenAllChildrenAreClosed() { @@ -251,7 +244,6 @@ void verifyContextReleasedWhenAllChildrenAreClosed() { } - @Test void verifyDownPropagationForChildrenOnDifferentThreadWithRootNotClosed() throws Exception { doReturn(true).when(propagation).isPropagatedDownWithinJVM(any()); @@ -262,7 +254,7 @@ void verifyDownPropagationForChildrenOnDifferentThreadWithRootNotClosed() throws root.setData("tag", "invisibleValue"); AtomicReference tagValue = new AtomicReference<>(); - Thread asyncTask = new Thread(Context.current().wrap(() -> { + Thread asyncTask = new Thread(ContextUtil.current().wrap(() -> { InspectitContextImpl asyncChild = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, false); tagValue.set(asyncChild.getData("tag")); asyncChild.makeActive(); @@ -277,7 +269,6 @@ void verifyDownPropagationForChildrenOnDifferentThreadWithRootNotClosed() throws assertThat(tagValue.get()).isEqualTo("rootValue"); } - @Test void verifyDownPropagationForChildrenOnDifferentThreadWithRootClosed() throws Exception { lenient().doReturn(true).when(propagation).isPropagatedDownWithinJVM(any()); @@ -288,7 +279,7 @@ void verifyDownPropagationForChildrenOnDifferentThreadWithRootClosed() throws Ex root.setData("tag", "invisibleValue"); AtomicReference tagValue = new AtomicReference<>(); - Thread asyncTask = new Thread(Context.current().wrap(() -> { + Thread asyncTask = new Thread(ContextUtil.current().wrap(() -> { InspectitContextImpl asyncChild = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, false); tagValue.set(asyncChild.getData("tag")); asyncChild.makeActive(); @@ -303,7 +294,6 @@ void verifyDownPropagationForChildrenOnDifferentThreadWithRootClosed() throws Ex assertThat(tagValue.get()).isEqualTo("rootValue"); } - @Test void verifyDownPropagationForChildrenOnSameThreadWithRootClosed() throws Exception { lenient().doReturn(true).when(propagation).isPropagatedDownWithinJVM(any()); @@ -314,7 +304,7 @@ void verifyDownPropagationForChildrenOnSameThreadWithRootClosed() throws Excepti root.setData("tag", "invisibleValue"); AtomicReference tagValue = new AtomicReference<>(); - Runnable delayedTask = Context.current().wrap(() -> { + Runnable delayedTask = ContextUtil.current().wrap(() -> { InspectitContextImpl delayedChild = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, false); tagValue.set(delayedChild.getData("tag")); delayedChild.makeActive(); @@ -330,7 +320,6 @@ void verifyDownPropagationForChildrenOnSameThreadWithRootClosed() throws Excepti } - @Nested public class UpPropagation { @@ -373,10 +362,9 @@ void verifyNewValuesPropagatedWhenConfigured() { ctxA.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyNoUpPropagationForChildrenOnDifferentThreadWithRootNotClosed() throws Exception { lenient().doReturn(true).when(propagation).isPropagatedUpWithinJVM(any()); @@ -385,7 +373,7 @@ void verifyNoUpPropagationForChildrenOnDifferentThreadWithRootNotClosed() throws root.setData("tag", "rootValue"); root.makeActive(); - Thread asyncTask = new Thread(Context.current().wrap(() -> { + Thread asyncTask = new Thread(ContextUtil.current().wrap(() -> { InspectitContextImpl asyncChild = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, false); asyncChild.setData("tag", "asyncChildValue"); asyncChild.makeActive(); @@ -400,7 +388,6 @@ void verifyNoUpPropagationForChildrenOnDifferentThreadWithRootNotClosed() throws assertThat(root.getData("tag")).isEqualTo("rootValue"); } - @Test void verifyNoUpPropagationForChildrenOnDifferentThreadWithRootClosed() throws Exception { lenient().doReturn(true).when(propagation).isPropagatedUpWithinJVM(any()); @@ -409,7 +396,7 @@ void verifyNoUpPropagationForChildrenOnDifferentThreadWithRootClosed() throws Ex root.setData("tag", "rootValue"); root.makeActive(); - Thread asyncTask = new Thread(Context.current().wrap(() -> { + Thread asyncTask = new Thread(ContextUtil.current().wrap(() -> { InspectitContextImpl asyncChild = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, false); asyncChild.setData("tag", "asyncChildValue"); asyncChild.makeActive(); @@ -424,7 +411,6 @@ void verifyNoUpPropagationForChildrenOnDifferentThreadWithRootClosed() throws Ex assertThat(root.getData("tag")).isEqualTo("rootValue"); } - @Test void verifyNoUpPropagationForChildrenOnSameThreadWithRootClosed() throws Exception { lenient().doReturn(true).when(propagation).isPropagatedUpWithinJVM(any()); @@ -433,7 +419,7 @@ void verifyNoUpPropagationForChildrenOnSameThreadWithRootClosed() throws Excepti root.setData("tag", "rootValue"); root.makeActive(); - Runnable delayedTask = Context.current().wrap(() -> { + Runnable delayedTask = ContextUtil.current().wrap(() -> { InspectitContextImpl delayedChild = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, false); delayedChild.setData("tag", "asyncChildValue"); delayedChild.makeActive(); @@ -448,7 +434,6 @@ void verifyNoUpPropagationForChildrenOnSameThreadWithRootClosed() throws Excepti } } - @Nested public class UpAndDownPropagation { @@ -508,7 +493,7 @@ void verifyComplexTracePropagation() { ctxA.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } @Test @@ -526,7 +511,7 @@ void verifyUpPropagatedValuesInvisibleForChildrenOnDifferentThreadWithRootNotClo syncChild.close(); AtomicReference asyncTaskTagValue = new AtomicReference<>(); - Thread asyncTask = new Thread(Context.current().wrap(() -> { + Thread asyncTask = new Thread(ContextUtil.current().wrap(() -> { InspectitContextImpl asyncChild = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, false); asyncTaskTagValue.set(asyncChild.getData("tag")); asyncChild.makeActive(); @@ -542,7 +527,6 @@ void verifyUpPropagatedValuesInvisibleForChildrenOnDifferentThreadWithRootNotClo assertThat(root.getData("tag")).isEqualTo("syncChildValue"); } - @Test void verifyUpPropagatedValuesInvisibleForChildrenOnDifferentThreadWithRootClosed() throws Exception { doReturn(true).when(propagation).isPropagatedUpWithinJVM(any()); @@ -558,7 +542,7 @@ void verifyUpPropagatedValuesInvisibleForChildrenOnDifferentThreadWithRootClosed syncChild.close(); AtomicReference asyncTaskTagValue = new AtomicReference<>(); - Thread asyncTask = new Thread(Context.current().wrap(() -> { + Thread asyncTask = new Thread(ContextUtil.current().wrap(() -> { InspectitContextImpl asyncChild = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, false); asyncTaskTagValue.set(asyncChild.getData("tag")); asyncChild.makeActive(); @@ -570,12 +554,10 @@ void verifyUpPropagatedValuesInvisibleForChildrenOnDifferentThreadWithRootClosed asyncTask.start(); asyncTask.join(); - assertThat(asyncTaskTagValue.get()).isEqualTo("rootValue"); assertThat(root.getData("tag")).isEqualTo("syncChildValue"); } - @Test void verifyUpPropagatedValuesInvisibleForChildrenOnSameThreadWithRootClosed() throws Exception { doReturn(true).when(propagation).isPropagatedUpWithinJVM(any()); @@ -591,7 +573,7 @@ void verifyUpPropagatedValuesInvisibleForChildrenOnSameThreadWithRootClosed() th syncChild.close(); AtomicReference asyncTaskTagValue = new AtomicReference<>(); - Runnable asyncTask = Context.current().wrap(() -> { + Runnable asyncTask = ContextUtil.current().wrap(() -> { InspectitContextImpl asyncChild = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, false); asyncTaskTagValue.set(asyncChild.getData("tag")); asyncChild.makeActive(); @@ -614,7 +596,8 @@ public class TagContextDownPropagation { void verifyTagsExtractedOnRoot() { doAnswer((invocation) -> PropagationMetaData.builder()).when(propagation).copy(); - TagContextBuilder tcb = Tags.getTagger().emptyBuilder() + TagContextBuilder tcb = Tags.getTagger() + .emptyBuilder() .putLocal(TagKey.create("myTag"), TagValue.create("myValue")); try (Scope tc = tcb.buildScoped()) { InspectitContextImpl ctxA = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, true); @@ -628,15 +611,15 @@ void verifyTagsExtractedOnRoot() { ctxA.close(); } - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyTagPropagationPreservedOnRoot() { doAnswer((invocation) -> PropagationMetaData.builder()).when(propagation).copy(); - TagContextBuilder tcb = Tags.getTagger().emptyBuilder() + TagContextBuilder tcb = Tags.getTagger() + .emptyBuilder() .putLocal(TagKey.create("myTag"), TagValue.create("myValue")); try (Scope tc = tcb.buildScoped()) { InspectitContextImpl ctxA = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, true); @@ -652,7 +635,7 @@ void verifyTagPropagationPreservedOnRoot() { ctxA.close(); } - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } @Test @@ -668,7 +651,8 @@ void verifyTagsExtractedWithinTrace() { root.makeActive(); - TagContextBuilder tcb = Tags.getTagger().emptyBuilder() + TagContextBuilder tcb = Tags.getTagger() + .emptyBuilder() .putLocal(TagKey.create("myTag"), TagValue.create("myValue")); try (Scope tc = tcb.buildScoped()) { InspectitContextImpl ctxA = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, true); @@ -683,7 +667,7 @@ void verifyTagsExtractedWithinTrace() { } root.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } @Test @@ -699,7 +683,8 @@ void verifyTagPropagationPreservedWithinTrace() { root.makeActive(); - TagContextBuilder tcb = Tags.getTagger().emptyBuilder() + TagContextBuilder tcb = Tags.getTagger() + .emptyBuilder() .putLocal(TagKey.create("myTag"), TagValue.create("myValue")); try (Scope tc = tcb.buildScoped()) { InspectitContextImpl ctxA = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, true); @@ -718,10 +703,9 @@ void verifyTagPropagationPreservedWithinTrace() { } root.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyTagSettingWithoutPropagationPreservedWithinTrace() { doAnswer((invocation) -> PropagationMetaData.builder() @@ -732,7 +716,8 @@ void verifyTagSettingWithoutPropagationPreservedWithinTrace() { InspectitContextImpl root = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, true); root.makeActive(); - TagContextBuilder tcb = Tags.getTagger().emptyBuilder() + TagContextBuilder tcb = Tags.getTagger() + .emptyBuilder() .putLocal(TagKey.create("myTag"), TagValue.create("myValue")); try (Scope tc = tcb.buildScoped()) { InspectitContextImpl ctxA = InspectitContextImpl.createFromCurrent(Collections.emptyMap(), propagation, true); @@ -751,10 +736,9 @@ void verifyTagSettingWithoutPropagationPreservedWithinTrace() { } root.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyDataOnlyPublishedAsTagWhenConfigured() { doReturn(true).when(propagation).isPropagatedDownWithinJVM(any()); @@ -770,10 +754,9 @@ void verifyDataOnlyPublishedAsTagWhenConfigured() { assertThat(getCurrentTagsAsMap()).containsEntry("my_tag", "tagValue"); root.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyDataTypesPreservedWithinTrace() { doReturn(true).when(propagation).isPropagatedDownWithinJVM(any()); @@ -786,7 +769,8 @@ void verifyDataTypesPreservedWithinTrace() { root.makeActive(); - TagContextBuilder tcb = Tags.getTagger().currentBuilder() + TagContextBuilder tcb = Tags.getTagger() + .currentBuilder() .putLocal(TagKey.create("myTag"), TagValue.create("myValue")); try (Scope tc = tcb.buildScoped()) { @@ -803,10 +787,9 @@ void verifyDataTypesPreservedWithinTrace() { } root.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyCommonTagsPublished() { HashMap tags = new HashMap<>(); @@ -823,10 +806,9 @@ void verifyCommonTagsPublished() { assertThat(getCurrentTagsAsMap()).containsEntry("tagB", "valueB"); ctx.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyLocalValuesPublishedCorrectly() { doReturn(false).when(propagation).isPropagatedDownWithinJVM(eq("local")); @@ -853,10 +835,9 @@ void verifyLocalValuesPublishedCorrectly() { ctx.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } - @Test void verifyUpPropagatedValuesOnlyAvailableInFullTagScope() { doReturn(true).when(propagation).isPropagatedDownWithinJVM(any()); @@ -896,7 +877,7 @@ void verifyUpPropagatedValuesOnlyAvailableInFullTagScope() { ctx.close(); - assertThat(InspectitContextImpl.INSPECTIT_KEY.get()).isNull(); + assertThat(ContextUtil.currentInspectitContext()).isNull(); } @Test @@ -936,7 +917,6 @@ void verifyOnlyPrimitiveDataUsedAsTag() { } } - @Nested public class SpanActivation { @@ -958,5 +938,4 @@ void verifySpanAttachedAndDetached() { } - } From 632a3fa787d89e0256a0b863e30d9c826fdbac0b Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 7 Jan 2022 08:50:38 +0100 Subject: [PATCH 24/86] Fixed tests in TraceSettingsTest by adjusting the span's expected status to OTEL StatusData --- .../instrumentation/tracing/TraceSettingsTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceSettingsTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceSettingsTest.java index 78c7dc5137..5326d37dbf 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceSettingsTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/TraceSettingsTest.java @@ -1,9 +1,9 @@ package rocks.inspectit.ocelot.instrumentation.tracing; -import io.opencensus.trace.Status; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.SpanId; import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.data.StatusData; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -409,7 +409,7 @@ void testWithoutErrorStatus() { //wait for the end marker, this ensures that all sampled spans are also exported assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withoutErrorStatus"); - assertThat(sp.getStatus()).isEqualTo(Status.OK); + assertThat(sp.getStatus()).isEqualTo(StatusData.unset()); })); } @@ -420,7 +420,7 @@ void testNullErrorStatus() { //wait for the end marker, this ensures that all sampled spans are also exported assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withErrorStatus"); - assertThat(sp.getStatus()).isEqualTo(Status.OK); + assertThat(sp.getStatus()).isEqualTo(StatusData.unset()); })); } @@ -431,7 +431,7 @@ void testFalseErrorStatus() { //wait for the end marker, this ensures that all sampled spans are also exported assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withErrorStatus"); - assertThat(sp.getStatus()).isEqualTo(Status.OK); + assertThat(sp.getStatus()).isEqualTo(StatusData.unset()); })); } @@ -442,7 +442,7 @@ void testNonNullErrorStatus() { //wait for the end marker, this ensures that all sampled spans are also exported assertTraceExported((spans) -> assertThat(spans).hasSize(1).anySatisfy((sp) -> { assertThat(sp.getName()).isEqualTo("TraceSettingsTest.withErrorStatus"); - assertThat(sp.getStatus()).isEqualTo(Status.UNKNOWN); + assertThat(sp.getStatus()).isEqualTo(StatusData.error()); })); } } From 66ee94ad8c0e1bae0225b137344531c15ec8f798 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 7 Jan 2022 08:58:44 +0100 Subject: [PATCH 25/86] Minor adjustments in ReflectionUtils and OpenCensusShimUtils --- .../core/utils/OpenCensusShimUtils.java | 8 +--- .../ocelot/core/utils/ReflectionUtils.java | 41 +++++++++++++++++-- .../core/util/OpenCensusShimUtilsTest.java | 3 +- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java index ba082e4c75..b480b034ee 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java @@ -41,12 +41,9 @@ public static boolean setOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(Trace tracerField = Class.forName("io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl") .getDeclaredField("OTEL_TRACER"); - // set static final field ReflectionUtils.setFinalStatic(tracerField, tracer); - // set to the current tracer - tracerField.set(null, tracer); return true; } catch (Exception e) { log.error("Failed to set OTEL_TRACER in OpenTelemetrySpanBuilderImpl", e); @@ -57,11 +54,8 @@ public static boolean setOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(Trace public static Tracer getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl() { Field tracerField = null; try { - tracerField = Class.forName("io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl") - .getDeclaredField("OTEL_TRACER"); // make the field accessible - tracerField.setAccessible(true); - + tracerField = ReflectionUtils.getFinalStaticFieldAndMakeAccessible(Class.forName("io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl"), "OTEL_TRACER", true); Tracer tracer = (Tracer) tracerField.get(null); return tracer; } catch (Exception e) { diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java index d7bdb2adfb..3d328a3e10 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java @@ -1,8 +1,12 @@ package rocks.inspectit.ocelot.core.utils; +import io.opentelemetry.api.trace.Tracer; +import lombok.extern.slf4j.Slf4j; + import java.lang.reflect.Field; import java.lang.reflect.Modifier; +@Slf4j public class ReflectionUtils { /** @@ -17,7 +21,6 @@ public class ReflectionUtils { */ public static Field makeFieldAccessibleAndRemoveFinal(Field field) throws NoSuchFieldException, IllegalAccessException { field.setAccessible(true); - Field modifiers = Field.class.getDeclaredField("modifiers"); modifiers.setAccessible(true); modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); @@ -26,14 +29,46 @@ public static Field makeFieldAccessibleAndRemoveFinal(Field field) throws NoSuch /** * Sets the value of the final static {@link Field} via reflection and access modification - * @param field The final static {@link Field} + * + * @param field The final static {@link Field} * @param newValue + * * @throws NoSuchFieldException * @throws IllegalAccessException */ public static void setFinalStatic(Field field, Object newValue) throws NoSuchFieldException, IllegalAccessException { - makeFieldAccessibleAndRemoveFinal(field); + field = makeFieldAccessibleAndRemoveFinal(field); field.set(null, newValue); } + /** + * Gets the final static field for the given {@link Class}, makes it accessible + * + * @param clazz The {@link Class} that contains the final static field + * @param fieldName The name of the field + * @param removeFinal Whether to remove the {@link Modifier#FINAL} modifier from the field + * + * @return + */ + public static Field getFinalStaticFieldAndMakeAccessible(Class clazz, String fieldName, boolean removeFinal) { + Field field = null; + try { + field = clazz.getDeclaredField(fieldName); + + // make field accessible + if (removeFinal) { + ReflectionUtils.makeFieldAccessibleAndRemoveFinal(field); + } else { + field.setAccessible(true); + } + + return field; + + } catch (Exception e) { + log.error("Failed to get field " + fieldName + " for class " + clazz.getName()); + return null; + } + } + + } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/OpenCensusShimUtilsTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/OpenCensusShimUtilsTest.java index cd6f5e4841..cf725beb8e 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/OpenCensusShimUtilsTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/OpenCensusShimUtilsTest.java @@ -12,7 +12,8 @@ public class OpenCensusShimUtilsTest extends SpringTestBase { @Test - void testUpdateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl() throws InterruptedException { + void testUpdateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl() throws ClassNotFoundException, IllegalAccessException { + Tracer tracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); // reset OTEL From 02cf139818aa2310f8e367478f51e25ade4fee5a Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 7 Jan 2022 13:56:21 +0100 Subject: [PATCH 26/86] Disabled tests that will not work until CustomSpanBuilder is fixed for OpenTelemetry (#1246) --- .../ocelot/instrumentation/tracing/AutoTracingTest.java | 3 ++- .../instrumentation/autotracing/CustomSpanBuilderTest.java | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/AutoTracingTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/AutoTracingTest.java index 3289fe89d5..af35347c41 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/AutoTracingTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/AutoTracingTest.java @@ -1,7 +1,7 @@ package rocks.inspectit.ocelot.instrumentation.tracing; -import io.opentelemetry.sdk.trace.data.SpanData; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import rocks.inspectit.ocelot.utils.TestUtils; @@ -11,6 +11,7 @@ import static org.assertj.core.api.Assertions.assertThat; +@Disabled // TODO: fix StackTraceSampling and AutoTracing with OTEL public class AutoTracingTest extends TraceTestBase { @BeforeAll diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/CustomSpanBuilderTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/CustomSpanBuilderTest.java index d5108c39ef..ef7717b9f9 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/CustomSpanBuilderTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/CustomSpanBuilderTest.java @@ -4,11 +4,13 @@ import io.opencensus.trace.Span; import io.opencensus.trace.Tracing; import io.opencensus.trace.samplers.Samplers; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +@Disabled // TODO: fix CustomSpanBuilder with OTEL public class CustomSpanBuilderTest { From ab1309db4f1067bda972a8a70e2c28cf2cc16b6b Mon Sep 17 00:00:00 2001 From: Marius Oehler Date: Mon, 10 Jan 2022 14:56:37 +0100 Subject: [PATCH 27/86] Fix tests and typo in docs --- .../ActionMetricsRecorderSysTest.java | 4 +++- .../config/ActionMetricsRecorderTest.yml | 15 ++++++++++----- .../docs/metrics/self-monitoring.md | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderSysTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderSysTest.java index 3c75120a79..13a42c96d5 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderSysTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/metrics/selfmonitoring/ActionMetricsRecorderSysTest.java @@ -20,8 +20,10 @@ public class ActionMetricsRecorderSysTest extends MetricsSysTestBase { private static final ViewManager viewManager = Stats.getViewManager(); + public static double blackhole; + public void trigger() { - System.out.println("trigger method"); + blackhole = Math.random(); } @Test diff --git a/inspectit-ocelot-agent/src/system-test/resources/config/ActionMetricsRecorderTest.yml b/inspectit-ocelot-agent/src/system-test/resources/config/ActionMetricsRecorderTest.yml index d896e1e41c..101032983d 100644 --- a/inspectit-ocelot-agent/src/system-test/resources/config/ActionMetricsRecorderTest.yml +++ b/inspectit-ocelot-agent/src/system-test/resources/config/ActionMetricsRecorderTest.yml @@ -1,26 +1,31 @@ # dummy scopes, actions, and rules for the ActionMetricsRecorderTestAgent inspectit: + # enbale metrics + self-monitoring: + action-metrics: + enabled: true + instrumentation: - scopes: + scopes: # the trigger() method in ActionMetricsRecorderTestAgent 's_actionmetrics_trigger': type: - name: 'rocks.inspectit.ocelot.metrics.selfmonitoring.ActionMetricsRecorderTestAgent' + name: 'rocks.inspectit.ocelot.metrics.selfmonitoring.ActionMetricsRecorderSysTest' methods: - name: 'trigger' actions: - # simple action to get the full qualified class name 'a_trigger_action': input: _class: 'Class' - value: '_class.getName()' + value-body: | + return _class.getName(); rules: - 'r_...': + 'r_ActionMetricsRecorderSysTest_trigger': scopes: 's_actionmetrics_trigger': true entry: diff --git a/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md b/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md index 3bbcda5136..4de65d6dee 100644 --- a/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md +++ b/inspectit-ocelot-documentation/docs/metrics/self-monitoring.md @@ -33,7 +33,7 @@ The recording of these metrics can be enabled using the following configuration: inspectit: self-monitoring: action-metrics: - enabled: false + enabled: true ``` Note: the action execution metrics are only recorded in case the self-monitoring metrics are enabled. \ No newline at end of file From 6cc58a7aaf8ab1f4ad66a8b4ca06770a2864701d Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 09:36:10 +0100 Subject: [PATCH 28/86] Fixed the HttpRemoteTracingTest by adjusting StackTraceSampler#createNormalSpan to manually rebuild the io.opentelemetry.api.trace.SpanContext from io.opencensus.trace.SpanContext and adding it to the io.opentelemetry.Context for downstream propagation --- .../tracing/HttpRemoteTracingTest.java | 4 +- .../inspectit/ocelot/utils/TestUtils.java | 19 +++++----- .../autotracing/StackTraceSampler.java | 37 ++++++++++++++----- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java index 887055c294..58539a7c79 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java @@ -93,11 +93,11 @@ void testPropagationViaServlet() throws Exception { assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { assertThat(sp.getName()).endsWith("HttpUrlConnectionTest.clientSpan"); assertThat(sp.getKind()).isEqualTo(SpanKind.CLIENT); - assertThat(sp.getParentSpanId()).isNull(); + assertThat(SpanId.isValid(sp.getParentSpanId())).isFalse(); }).anySatisfy((sp) -> { assertThat(sp.getName()).endsWith("TracingServlet.myHandler"); assertThat(sp.getKind()).isEqualTo(SpanKind.SERVER); - assertThat(sp.getParentSpanId()).isNotNull(); + assertThat(SpanId.isValid(sp.getParentSpanId())).isTrue(); }) ); diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java index 4b05346e2c..21cca8871f 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java @@ -19,6 +19,7 @@ import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.extension.trace.propagation.B3Propagator; import io.opentelemetry.extension.trace.propagation.JaegerPropagator; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.resources.Resource; @@ -123,14 +124,14 @@ private static synchronized Cache, Object> getInstrumentationCache() { * This does not wait for potential hooks which will be created. */ public static void waitForClassInstrumentations(Class... clazz) { - waitForClassInstrumentations(Arrays.asList(clazz), false, 15, TimeUnit.SECONDS); + waitForClassInstrumentations(Arrays.asList(clazz), false, 15, TimeUnit.MINUTES); } /** * Waits until a hook for each of the given classes exist. */ public static void waitForClassHooks(Class... clazz) { - waitForClassHooks(Arrays.asList(clazz), 10, TimeUnit.SECONDS); + waitForClassHooks(Arrays.asList(clazz), 10, TimeUnit.MINUTES); } /** @@ -306,20 +307,18 @@ public static TimeSeries getTimeseries(String metricName, Map ta } /** - * Initialize {@link io.opentelemetry.api.OpenTelemetry} with a {@link InMemorySpanExporter} so that we can access the exported {@link io.opentelemetry.api.trace.Span}s + * Initialize {@link io.opentelemetry.api.OpenTelemetry} with a {@link InMemorySpanExporter} so that we can access the exported {@link io.opentelemetry.api.trace.Span Spans} * - * @return The {@link InMemorySpanExporter} that can be used to retrieve exported {@link io.opentelemetry.api.trace.Span}s + * @return The {@link InMemorySpanExporter} that can be used to retrieve exported {@link io.opentelemetry.api.trace.Span Spans} */ public static InMemorySpanExporter initializeOpenTelemetryForSystemTesting() { - - System.out.println("initialize"); // create an SdkTracerProvider with InMemorySpanExporter and LoggingSpanExporter - InMemorySpanExporter spanExporter = InMemorySpanExporter.create(); + InMemorySpanExporter inMemSpanExporter = InMemorySpanExporter.create(); SdkTracerProvider tracerProvider = SdkTracerProvider.builder() .setSampler(Sampler.alwaysOn()) - .addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build()) + .addSpanProcessor(BatchSpanProcessor.builder(inMemSpanExporter).build()) .addSpanProcessor(SimpleSpanProcessor.create(new LoggingSpanExporter())) .setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "rocks.inspectit.ocelot.system.test"))) .build(); @@ -328,7 +327,7 @@ public static InMemorySpanExporter initializeOpenTelemetryForSystemTesting() { OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) - .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance()))) + .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) .buildAndRegisterGlobal(); // set the OTEL_TRACER in OpenTelemetrySpanBuilderImpl via reflection. @@ -351,7 +350,7 @@ public static InMemorySpanExporter initializeOpenTelemetryForSystemTesting() { System.err.println("Failed to set OTEL_TRACER in OpenTelemetrySpanBuilderImpl"); } - return spanExporter; + return inMemSpanExporter; } private static long getInstrumentationQueueLength() { diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/StackTraceSampler.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/StackTraceSampler.java index 0934ecb522..1c7745de9b 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/StackTraceSampler.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/autotracing/StackTraceSampler.java @@ -3,6 +3,9 @@ import io.opencensus.common.Clock; import io.opencensus.common.Scope; import io.opencensus.trace.*; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.api.trace.TraceStateBuilder; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; @@ -91,8 +94,7 @@ public enum Mode { void init() { exportTask = executor.scheduleWithFixedDelay(this::doExportTraces, EXPORT_INTERVAL_MILLIS, EXPORT_INTERVAL_MILLIS, TimeUnit.MILLISECONDS); AutoTracingSettings settings = env.getCurrentConfig().getTracing().getAutoTracing(); - sampleTimer = new HighPrecisionTimer("Ocelot stack trace sampler", settings.getFrequency(), - settings.getShutdownDelay(), this::doSample); + sampleTimer = new HighPrecisionTimer("Ocelot stack trace sampler", settings.getFrequency(), settings.getShutdownDelay(), this::doSample); } @EventListener(InspectitConfigChangedEvent.class) @@ -203,9 +205,8 @@ private AutoCloseable createPauseAdjustingContext(Mode mode, SampledTrace active } private AutoCloseable continueSamplingAwareSpan(Span spanToContinue, MethodReflectionInformation actualMethod, SampledTrace activeSampling) { - SampledTrace.MethodExitNotifier exitCallback = activeSampling - .spanContinued(spanToContinue, clock.nowNanos(), actualMethod.getDeclaringClass() - .getName(), actualMethod.getName()); + SampledTrace.MethodExitNotifier exitCallback = activeSampling.spanContinued(spanToContinue, clock.nowNanos(), actualMethod.getDeclaringClass() + .getName(), actualMethod.getName()); Scope ctx = Tracing.getTracer().withSpan(spanToContinue); return () -> { ctx.close(); @@ -244,8 +245,8 @@ private AutoCloseable createSamplingAwareSpan(String name, SpanContext remotePar parent = Tracing.getTracer().getCurrentSpan().getContext(); } PlaceholderSpan span = new PlaceholderSpan(parent, name, kind, clock::nowNanos); - SampledTrace.MethodExitNotifier exitCallback = activeSampling - .newSpanStarted(span, actualMethod.getDeclaringClass().getName(), actualMethod.getName()); + SampledTrace.MethodExitNotifier exitCallback = activeSampling.newSpanStarted(span, actualMethod.getDeclaringClass() + .getName(), actualMethod.getName()); Scope ctx = Tracing.getTracer().withSpan(span); return () -> { ctx.close(); @@ -256,7 +257,24 @@ private AutoCloseable createSamplingAwareSpan(String name, SpanContext remotePar private Span createNormalSpan(String name, SpanContext remoteParent, Sampler sampler, Span.Kind kind) { SpanBuilder builder; if (remoteParent != null) { - builder = Tracing.getTracer().spanBuilderWithRemoteParent(name, remoteParent); + // rebuild the io.opentelemetry.api.trace.SpanContext manually from OC remoteParent span context + // this is done because Tracing.getTracer.spanBuilderWithRemoteParent does not work as expected. The remote parent span is **not** added to the OTEL context (see io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl#startSpan and io.opentelemetry.sdk.trace.SdkSpanBuilder#startSpan) + TraceStateBuilder tsBuilder = TraceState.builder(); + remoteParent.getTracestate().getEntries().forEach(entry -> tsBuilder.put(entry.getKey(), entry.getValue())); + TraceState traceState = tsBuilder.build(); + + // rebuild SpanContext + io.opentelemetry.api.trace.SpanContext fromRemoteParent = io.opentelemetry.api.trace.SpanContext.createFromRemoteParent(remoteParent.getTraceId() + .toLowerBase16(), remoteParent.getSpanId().toLowerBase16(), remoteParent.getTraceOptions() + .isSampled() ? TraceFlags.getSampled() : TraceFlags.getDefault(), traceState); + + // get a non-recording OTEL span from the manually rebuilt span context + io.opentelemetry.api.trace.Span span = io.opentelemetry.api.trace.Span.wrap(fromRemoteParent); + try (// add the span to io.openTelemetry.Context + io.opentelemetry.context.Scope scope = span.makeCurrent()) { + // get the SpanBuilder + builder = Tracing.getTracer().spanBuilder(name); + } } else { builder = Tracing.getTracer().spanBuilder(name); } @@ -276,7 +294,8 @@ private boolean doSample() { //copy the map to avoid concurrent modifications due to the starting and ending of sampling traces Map samplingsCopy = new HashMap<>(activeSamplings); - Set threadsToSample = samplingsCopy.keySet().stream() + Set threadsToSample = samplingsCopy.keySet() + .stream() .filter(trace -> !samplingsCopy.get(trace).isPaused()) .collect(Collectors.toSet()); From 5853efa640282f5c491450cd16a7b07c574f0173 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 09:37:40 +0100 Subject: [PATCH 29/86] Added a stricter checker to ensure OTEL scopes are closed on the correct thread and are not garbage collected before they are closed. --- inspectit-ocelot-agent/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/inspectit-ocelot-agent/build.gradle b/inspectit-ocelot-agent/build.gradle index 306eaf8dc5..eb667a91bd 100644 --- a/inspectit-ocelot-agent/build.gradle +++ b/inspectit-ocelot-agent/build.gradle @@ -232,6 +232,10 @@ task systemTest { jvmArgs "-javaagent:${agentJarPath}" // Our agent second. jacoco.enabled = false // Don't add the JaCoCo agent again. + // stricter checker makes sure that {@link Scope}s are closed on the correct thread and + // that they are not garbage collected before being closed. + jvmArgs "-Dio.opentelemetry.context.enableStrictContext=true" + } doFirst { logger.lifecycle("Running system tests using ${javaExecutable}.") } From 03266265962f46e1f37f52e30e67d25504d02b66 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 09:44:49 +0100 Subject: [PATCH 30/86] Minor adjustments in ReflectionUtils --- .../ocelot/core/utils/ReflectionUtils.java | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java index 3d328a3e10..9f2b7aae0f 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java @@ -1,6 +1,5 @@ package rocks.inspectit.ocelot.core.utils; -import io.opentelemetry.api.trace.Tracer; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Field; @@ -44,31 +43,45 @@ public static void setFinalStatic(Field field, Object newValue) throws NoSuchFie /** * Gets the final static field for the given {@link Class}, makes it accessible * - * @param clazz The {@link Class} that contains the final static field - * @param fieldName The name of the field + * @param clazz The {@link Class} that contains the final static field + * @param fieldName The name of the field * @param removeFinal Whether to remove the {@link Modifier#FINAL} modifier from the field * * @return + * + * @throws NoSuchFieldException + * @throws IllegalAccessException */ - public static Field getFinalStaticFieldAndMakeAccessible(Class clazz, String fieldName, boolean removeFinal) { - Field field = null; - try { - field = clazz.getDeclaredField(fieldName); - // make field accessible - if (removeFinal) { - ReflectionUtils.makeFieldAccessibleAndRemoveFinal(field); - } else { - field.setAccessible(true); - } + public static Field getFinalStaticFieldAndMakeAccessible(Class clazz, String fieldName, boolean removeFinal) throws NoSuchFieldException, IllegalAccessException { + Field field = null; - return field; + field = clazz.getDeclaredField(fieldName); - } catch (Exception e) { - log.error("Failed to get field " + fieldName + " for class " + clazz.getName()); - return null; + // make field accessible + if (removeFinal) { + ReflectionUtils.makeFieldAccessibleAndRemoveFinal(field); + } else { + field.setAccessible(true); } + + return field; } + /** + * Gets the {@link Field} for the given private field of the given {@link Class} + * + * @param clazz The {@link Class} + * @param fieldName The name of the private field + * + * @return + * + * @throws NoSuchFieldException + */ + public static Field getPrivateField(Class clazz, String fieldName) throws NoSuchFieldException { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } } From 977d3cb6e5ee7746576a810109fbe98e18de9788 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 10:23:26 +0100 Subject: [PATCH 31/86] Removed obsolete TODO note from HttpRemoteTracingTest and adjusted a comment in exporters.yml --- .../ocelot/instrumentation/tracing/HttpRemoteTracingTest.java | 2 -- .../rocks/inspectit/ocelot/config/default/exporters.yml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java index 58539a7c79..7f5c4799b5 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/tracing/HttpRemoteTracingTest.java @@ -131,8 +131,6 @@ void testPropagationViaServlet() throws Exception { TestUtils.waitForClassInstrumentations(Arrays.asList(CloseableHttpClient.class, Class.forName("org.apache.http.impl.client.InternalHttpClient"), ApacheClientConnectionTest.class, TracingServlet.class), true, 15, TimeUnit.SECONDS); clientSpan(); - // TODO: these tests fail as the spans are not belonging to the same trace - assertTraceExported((spans) -> assertThat(spans).anySatisfy((sp) -> { assertThat(sp.getName()).endsWith("ApacheClientConnectionTest.clientSpan"); assertThat(sp.getKind()).isEqualTo(SpanKind.CLIENT); diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml index 548026b588..434439daec 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml @@ -90,7 +90,7 @@ inspectit: # the time at which the exporter tries to reconnect to the OpenCensus agent reconnection-period: 5s - # settings for the SpanLoggingExporterService + # settings for the LoggingSpanExporter used in LoggingTraceExporterService logging: enabled: true service-name: ${inspectit.service-name} From 61baa64bcbb16ffc0c12faa234d0fb2939a09fd1 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 10:46:40 +0100 Subject: [PATCH 32/86] Disabled tests for service exporters that are not supported with the current version (##1246) --- .../ocelot/core/exporter/JaegerExporterServiceIntTest.java | 2 ++ .../ocelot/core/exporter/PrometheusExporterServiceIntTest.java | 2 ++ .../ocelot/core/exporter/ZipkinExporterServiceIntTest.java | 2 ++ 3 files changed, 6 insertions(+) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java index 0204f150f9..4f54ed0898 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java @@ -5,6 +5,7 @@ import io.opencensus.trace.samplers.Samplers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,6 +20,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.awaitility.Awaitility.await; +@Disabled // TODO: fix JaegerExporterService with OTEL @TestPropertySource(properties = { "inspectit.exporters.tracing.jaeger.url=http://127.0.0.1:14268/api/traces" }) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java index 0d47e54c6d..854ff6c74b 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java @@ -7,6 +7,7 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.core.SpringTestBase; @@ -16,6 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; +@Disabled // TODO: fix PrometheusExporterService with OTEL public class PrometheusExporterServiceIntTest extends SpringTestBase { private static final int HTTP_TIMEOUT = 1000; diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java index 74af00f51b..26365a230d 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java @@ -5,6 +5,7 @@ import io.opencensus.trace.samplers.Samplers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,6 +20,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.awaitility.Awaitility.await; +@Disabled // TODO: fix ZipkinExporterService with OTEL @TestPropertySource(properties = {"inspectit.exporters.tracing.zipkin.url=http://127.0.0.1:9411/api/v2/spans"}) @DirtiesContext public class ZipkinExporterServiceIntTest extends SpringTestBase { From a65e2783b5590fbf748f822cbb79a99df9784862 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 10:47:13 +0100 Subject: [PATCH 33/86] Minor refactoring of LoggingMetricExporterService and LoggingTraceExporterService (#1246) --- .../LoggingMetricExporterService.java | 8 ++----- .../exporter/LoggingTraceExporterService.java | 22 +++++++++---------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java index 41157ecc52..67b2868ede 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java @@ -38,11 +38,6 @@ public class LoggingMetricExporterService extends DynamicallyActivatableService */ private PeriodicMetricReaderBuilder metricReader; - /** - * The service name {@link Resource} - */ - private Resource serviceNameResource; - public LoggingMetricExporterService() { super("exporters.metrics.logging", "metrics.enabled"); } @@ -93,10 +88,11 @@ protected boolean doEnable(InspectitConfig configuration) { @Override protected boolean doDisable() { try { + // close the meter provider if (null != meterProvider) { // flush all metrics before disabling them meterProvider.forceFlush(); - meterProvider.shutdown(); + meterProvider.close(); meterProvider = null; } metricExporter.doDisable(); diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java index 76c24f8d78..904eecca55 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java @@ -8,12 +8,14 @@ import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.extension.trace.propagation.B3Propagator; import io.opentelemetry.extension.trace.propagation.JaegerPropagator; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.sdk.trace.samplers.Sampler; import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -42,9 +44,9 @@ public class LoggingTraceExporterService extends DynamicallyActivatableService { private SdkTracerProvider tracerProvider; /** - * The {@link DynamicallyActivatableSampler} for the {@link #tracerProvider} + * The {@link io.opentelemetry.sdk.trace.samplers.Sampler} for the {@link #tracerProvider} */ - private DynamicallyActivatableSampler sampler; + private Sampler sampler; /** * The {@link SpanProcessor} of the {@link #spanExporter @@ -100,9 +102,7 @@ protected boolean doEnable(InspectitConfig conf) { simpleSpanProcessor = SimpleSpanProcessor.create(spanExporter); // create sampler - sampler = DynamicallyActivatableSampler.createRatio(env.getCurrentConfig() - .getTracing() - .getSampleProbability()); + sampler = Sampler.traceIdRatioBased(env.getCurrentConfig().getTracing().getSampleProbability()); // create Resource for the service name serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, logging.getServiceName())); @@ -117,9 +117,7 @@ protected boolean doEnable(InspectitConfig conf) { // build and register OTel openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) - .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance()))) - // TODO: do I also need the W3CBaggagePropagator? - // W3CBaggagePropagator.getInstance() + .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) .buildAndRegisterGlobal(); // update OC tracer @@ -129,7 +127,6 @@ protected boolean doEnable(InspectitConfig conf) { spanExporter.doEnable(); log.info("Starting TraceLoggingSpanExporter"); - return true; } catch (Exception e) { log.error("Failed to start TraceLoggingExporter", e); @@ -141,10 +138,13 @@ protected boolean doEnable(InspectitConfig conf) { protected boolean doDisable() { try { // disable the span exporter - if (null != spanExporter && null != tracerProvider) { + if (null != spanExporter) { spanExporter.doDisable(); + } + // close the tracerProvider + if (null != tracerProvider) { tracerProvider.forceFlush(); - tracerProvider.shutdown(); + tracerProvider.close(); tracerProvider = null; } log.info("Stopping TraceLoggingSpanExporter"); From 37bdc0a7d11437944785c5d9828daa3ee701830e Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 11:05:48 +0100 Subject: [PATCH 34/86] #1269: Integrated the concept of an IOpenTelemetryController that handles the registration and unregister of individual traces and metrics exporter services. Fixed JaegerExporterService, PrometheusExporterService, ZipkinExporterService to work with inspectIT Ocelot. Adjusted build.gradle in the core package and added a SpringTestBase.yml configuration to speed up tests in the core module (as the OTEL implementation of the Prometheus server needs ~10 seconds to shut down). --- .../inspectit/ocelot/bootstrap/Instances.java | 4 + .../IOpenTelemetryController.java | 43 ++ .../NoopOpenTelemetryController.java | 36 ++ .../trace/OtlpTraceExporterSettings.java | 21 + .../trace/TraceExportersSettings.java | 3 + .../ocelot/config/default/exporters.yml | 8 +- inspectit-ocelot-core/build.gradle | 14 +- .../BootstrapInitializerConfiguration.java | 10 + ...allyActivatableMetricsExporterService.java | 35 ++ .../DynamicallyActivatableSampler.java | 5 + .../DynamicallyActivatableSpanExporter.java | 103 +++- .../DynamicallyActivatableSpanProcessor.java | 57 +- ...icallyActivatableTraceExporterService.java | 35 ++ .../core/exporter/JaegerExporterService.java | 62 ++- .../LoggingMetricExporterService.java | 57 +- .../exporter/LoggingTraceExporterService.java | 110 +--- .../exporter/OtlpTraceExporterService.java | 87 +++ .../exporter/PrometheusExporterService.java | 32 +- .../core/exporter/SpanExporterImpl.java | 54 ++ .../core/exporter/ZipkinExporterService.java | 48 +- .../CustomMetricReaderFactory.java | 44 ++ .../core/opentelemetry/MeterProviderImpl.java | 77 +++ .../OpenTelemetryControllerImpl.java | 526 ++++++++++++++++++ .../core/opentelemetry/OpenTelemetryImpl.java | 87 +++ .../DynamicallyActivatableService.java | 15 + .../ocelot/core/utils/OpenTelemetryUtils.java | 120 ++++ .../JaegerExporterServiceIntTest.java | 3 +- .../LoggingMetricsExporterServiceIntTest.java | 5 + .../LoggingTraceExporterServiceIntTest.java | 73 +-- .../PrometheusExporterServiceIntTest.java | 32 +- .../test/resources/config/SpringTestBase.yml | 6 + 31 files changed, 1598 insertions(+), 214 deletions(-) create mode 100644 inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java create mode 100644 inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricsExporterService.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/SpanExporterImpl.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/CustomMetricReaderFactory.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java create mode 100644 inspectit-ocelot-core/src/test/resources/config/SpringTestBase.yml diff --git a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/Instances.java b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/Instances.java index 67c645cb57..95c35453d4 100644 --- a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/Instances.java +++ b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/Instances.java @@ -10,6 +10,8 @@ import rocks.inspectit.ocelot.bootstrap.instrumentation.IHookManager; import rocks.inspectit.ocelot.bootstrap.instrumentation.noop.NoopHookManager; import rocks.inspectit.ocelot.bootstrap.instrumentation.noop.NoopObjectAttachments; +import rocks.inspectit.ocelot.bootstrap.opentelemetry.IOpenTelemetryController; +import rocks.inspectit.ocelot.bootstrap.opentelemetry.NoopOpenTelemetryController; import java.net.URL; @@ -41,4 +43,6 @@ public class Instances { public static LogTraceCorrelator logTraceCorrelator = NoopLogTraceCorrelator.INSTANCE; public static TraceIdInjector traceIdInjector = NoopTraceIdInjector.INSTANCE; + + public static IOpenTelemetryController openTelemetryController = NoopOpenTelemetryController.INSTANCE; } diff --git a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java new file mode 100644 index 0000000000..874cdb0446 --- /dev/null +++ b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java @@ -0,0 +1,43 @@ +package rocks.inspectit.ocelot.bootstrap.opentelemetry; + +/** + * Controller interface for the OpenTelemetryController. Its implementation is {@link rocks.inspectit.ocelot.core.opentelemetry.OpenTelemetryControllerImpl} + */ +public interface IOpenTelemetryController { + + /** + * Shuts down the {@link IOpenTelemetryController} + */ + void shutdown(); + + /** + * Starts the {@link IOpenTelemetryController} + * + * @return Whether the {@link IOpenTelemetryController} was successfuly started + */ + boolean start(); + + /** + * Gets whether the {@link IOpenTelemetryController} is configured + * + * @return Whether the {@link IOpenTelemetryController} is configured + */ + boolean isConfigured(); + + /** + * Gets whether the {@link IOpenTelemetryController} is enabled + * + * @return Whether the {@link IOpenTelemetryController} is enabled + */ + boolean isEnabled(); + + /** + * Notifies the {@link IOpenTelemetryController} that something in the {@link rocks.inspectit.ocelot.config.model.tracing.TracingSettings} changed + */ + void notifyTracingSettingsChanged(); + + /** + * Notifies the {@link IOpenTelemetryController} that something in the {@link rocks.inspectit.ocelot.config.model.metrics.MetricsSettings} changed + */ + void notifyMetricsSettingsChanged(); +} diff --git a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java new file mode 100644 index 0000000000..08fd723e5d --- /dev/null +++ b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java @@ -0,0 +1,36 @@ +package rocks.inspectit.ocelot.bootstrap.opentelemetry; + +public class NoopOpenTelemetryController implements IOpenTelemetryController { + + public static final NoopOpenTelemetryController INSTANCE = new NoopOpenTelemetryController(); + + @Override + public void shutdown() { + + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean isConfigured() { + return false; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public void notifyTracingSettingsChanged() { + + } + + @Override + public void notifyMetricsSettingsChanged() { + + } +} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java new file mode 100644 index 0000000000..6ab0589241 --- /dev/null +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java @@ -0,0 +1,21 @@ +package rocks.inspectit.ocelot.config.model.exporters.trace; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Settings for {@link rocks.inspectit.ocelot.core.exporter.OtlpTraceExporterService} + */ +@Data +@NoArgsConstructor +public class OtlpTraceExporterSettings { + + private boolean enabled; + + /*** + * The OTLP traces endpoint to connect to + */ + private String url; + + private String serviceName; +} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java index 16c2a2009f..d054d51d0f 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java @@ -20,4 +20,7 @@ public class TraceExportersSettings { @Valid private LoggingTraceExporterSettings logging; + + @Valid + private OtlpTraceExporterSettings otlp; } diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml index 548026b588..881aa2ba10 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml @@ -90,7 +90,13 @@ inspectit: # the time at which the exporter tries to reconnect to the OpenCensus agent reconnection-period: 5s - # settings for the SpanLoggingExporterService + # settings for the LoggingSpanExporter used in LoggingTraceExporterService logging: enabled: true service-name: ${inspectit.service-name} + + # settings for the OtlpGrpcSpanExporter used in OtlpTraceExporterService + otlp: + enabled: true + service-name: ${inspectit.service-name} + url: null diff --git a/inspectit-ocelot-core/build.gradle b/inspectit-ocelot-core/build.gradle index d20cf05e59..b9e7994ac0 100644 --- a/inspectit-ocelot-core/build.gradle +++ b/inspectit-ocelot-core/build.gradle @@ -13,6 +13,9 @@ test { jvmArgs '-Xmx512m', '-XX:+HeapDumpOnOutOfMemoryError', '-XX:HeapDumpPath=/__w/inspectit-ocelot/inspectit-ocelot/test_heapdump.bin' + // include custom configurations to potentially speed up tests or change settings in test files + jvmArgs "-Dinspectit.config.file-based.path=$projectDir/src/test/resources/config" + testLogging { exceptionFormat = 'full' } @@ -84,6 +87,13 @@ dependencies { // OpenTelemetry exporters platform("io.opentelemetry:opentelemetry-bom:${openTelemetryVersion}"), "io.opentelemetry:opentelemetry-exporter-logging", + "io.opentelemetry:opentelemetry-exporter-jaeger", + "io.opentelemetry:opentelemetry-exporter-jaeger-thrift", + "io.opentelemetry:opentelemetry-exporter-zipkin", + "io.opentelemetry:opentelemetry-exporter-otlp", + + platform("io.opentelemetry:opentelemetry-bom-alpha:${openTelemetryAlphaVersion}"), + "io.opentelemetry:opentelemetry-exporter-prometheus", // The following dependency is required for the OC-exporter to work correctly and must be matched against the grpc version // See https://github.com/census-instrumentation/opencensus-java/blob/master/exporters/trace/ocagent/README.md @@ -134,7 +144,9 @@ task checkDependencyJavaVersions { // exclude OpenTelemetry as they guarantee JDK 8 support "opentelemetry", // exclude jackson which is being used by OTel - "jackson"] + "jackson", + // exclude kotlin-stdlib which is being used by opentelemetry-exporter-jaeger-thrift + "kotlin-stdlib"] def jarCheckPath = "$buildDir/jarCheck" outputs.dir jarCheckPath diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/spring/BootstrapInitializerConfiguration.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/spring/BootstrapInitializerConfiguration.java index 53e20963a7..a0ab6fdca0 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/spring/BootstrapInitializerConfiguration.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/spring/BootstrapInitializerConfiguration.java @@ -1,5 +1,6 @@ package rocks.inspectit.ocelot.core.config.spring; +import org.hibernate.validator.internal.constraintvalidators.hv.LengthValidator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import rocks.inspectit.ocelot.bootstrap.Instances; @@ -8,6 +9,7 @@ import rocks.inspectit.ocelot.bootstrap.correlation.noop.NoopTraceIdInjector; import rocks.inspectit.ocelot.bootstrap.instrumentation.noop.NoopHookManager; import rocks.inspectit.ocelot.bootstrap.instrumentation.noop.NoopObjectAttachments; +import rocks.inspectit.ocelot.bootstrap.opentelemetry.NoopOpenTelemetryController; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import rocks.inspectit.ocelot.core.instrumentation.config.InstrumentationConfigurationResolver; @@ -15,6 +17,7 @@ import rocks.inspectit.ocelot.core.instrumentation.context.ObjectAttachmentsImpl; import rocks.inspectit.ocelot.core.instrumentation.correlation.log.LogTraceCorrelatorImpl; import rocks.inspectit.ocelot.core.instrumentation.correlation.log.MdcAccessManager; +import rocks.inspectit.ocelot.core.opentelemetry.OpenTelemetryControllerImpl; import rocks.inspectit.ocelot.core.tags.CommonTagsManager; import javax.annotation.PreDestroy; @@ -52,6 +55,12 @@ public LogTraceCorrelatorImpl getLogTraceCorrelator(MdcAccessManager mdcAccessMa return new LogTraceCorrelatorImpl(mdcAccessManager, traceIdKey); } + @Bean(OpenTelemetryControllerImpl.BEAN_NAME) + public OpenTelemetryControllerImpl getOpenTelemetryController(InspectitEnvironment environment) { + InspectitConfig configuration = environment.getCurrentConfig(); + return new OpenTelemetryControllerImpl(); + } + @PreDestroy void destroy() { Instances.contextManager = NoopContextManager.INSTANCE; @@ -59,5 +68,6 @@ void destroy() { Instances.hookManager = NoopHookManager.INSTANCE; Instances.logTraceCorrelator = NoopLogTraceCorrelator.INSTANCE; Instances.traceIdInjector = NoopTraceIdInjector.INSTANCE; + Instances.openTelemetryController = NoopOpenTelemetryController.INSTANCE; } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricsExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricsExporterService.java new file mode 100644 index 0000000000..83797e9820 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricsExporterService.java @@ -0,0 +1,35 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.MetricReader; +import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.metrics.PrometheusExporterSettings; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; + +/** + * Base class for metrics export services that can be dynamically enabled and disabled based on the {@link InspectitConfig}. + * This class extends {@link DynamicallyActivatableService} which handles the waiting for changes in the configuration. + */ +public abstract class DynamicallyActivatableMetricsExporterService extends DynamicallyActivatableService { + + /** + * Gets a {@link MetricReaderFactory} for this service. + * + * @return + */ + public abstract MetricReaderFactory getMetricReaderFactory(); + + /** + * Constructor. + * + * @param configDependencies The list of configuration properties in camelCase this service depends on. + * For example "exporters.metrics.prometheus" specifies a dependency + * to {@link PrometheusExporterSettings} + * and all its children. + */ + public DynamicallyActivatableMetricsExporterService(String... configDependencies) { + super(configDependencies); + } + +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java index 67a5ebb4ed..b215a30b78 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java @@ -48,6 +48,11 @@ public static DynamicallyActivatableSampler createParentBased(Sampler root) { return new DynamicallyActivatableSampler(Sampler.parentBased(root)); } + /** + * Returns a new {@link DynamicallyActivatableSampler} with a {@link io.opentelemetry.sdk.trace.samplers.TraceIdRatioBasedSampler} implementation + * @param ratio The desired ratio of sampling. Must be within [0.0, 1.0]. + * @return A new {@link DynamicallyActivatableSampler} with a {@link io.opentelemetry.sdk.trace.samplers.TraceIdRatioBasedSampler} implementation. + */ public static DynamicallyActivatableSampler createRatio(double ratio) { return new DynamicallyActivatableSampler(Sampler.traceIdRatioBased(ratio)); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java index dee4ca2c49..1f703b2997 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java @@ -10,6 +10,10 @@ import lombok.extern.slf4j.Slf4j; import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** * A custom {@link SpanExporter} wrapper that can be dynamically enabled or disabled. @@ -18,6 +22,8 @@ @Slf4j public class DynamicallyActivatableSpanExporter implements SpanExporter { + ReadWriteLock lock = new ReentrantReadWriteLock(); + @Setter(AccessLevel.PRIVATE) private boolean enabled; @@ -30,15 +36,17 @@ private DynamicallyActivatableSpanExporter(T spanExporter) { exporter = spanExporter; } + private Map serviceHandlers = new ConcurrentHashMap<>(); + /** - * Creates a new {@link DynamicallyActivatableSpanExporter} with the given {@link SpanExporter} as the implemented exporter + * Creates a new {@link DynamicallyActivatableSpanExporter} with the given {@link SpanExporter}s as the implemented exporter * - * @param spanExporter + * @param spanExporters * - * @return {@link DynamicallyActivatableSpanExporter} with the given {@link SpanExporter} as the implemented exporter + * @return {@link DynamicallyActivatableSpanExporter} with the given {@link SpanExporter}s as the implemented exporter */ - public static DynamicallyActivatableSpanExporter create(SpanExporter spanExporter) { - return new DynamicallyActivatableSpanExporter(spanExporter); + public static DynamicallyActivatableSpanExporter create(SpanExporter... spanExporters) { + return new DynamicallyActivatableSpanExporter(SpanExporter.composite(spanExporters)); } /** @@ -54,7 +62,15 @@ public static DynamicallyActivatableSpanExporter createLogg public CompletableResultCode export(Collection spans) { // if enabled, call the real exporter's export method if (isEnabled()) { - return exporter.export(spans); + for (Handler handler : serviceHandlers.values()) { + handler.export(spans); + } + lock.readLock().lock(); + try { + return exporter.export(spans); + } finally { + lock.readLock().unlock(); + } } // otherwise, do nothing and return success else { @@ -64,12 +80,30 @@ public CompletableResultCode export(Collection spans) { @Override public CompletableResultCode flush() { - return exporter.flush(); + // TODO: implement own flush method?? , e.g., + lock.readLock().lock(); + try { + return exporter.flush(); + } catch (Exception e) { + log.error("failed to flush " + exporter.getClass(), e); + return CompletableResultCode.ofFailure(); + } finally { + lock.readLock().unlock(); + } } @Override public CompletableResultCode shutdown() { - return exporter.shutdown(); + doDisable(); + lock.readLock().lock(); + try { + return exporter.shutdown(); + } catch (Exception e) { + log.error("failed to shutdown " + exporter.getClass(), e); + return CompletableResultCode.ofFailure(); + } finally { + lock.readLock().unlock(); + } } public boolean doEnable() { @@ -82,4 +116,57 @@ public boolean doDisable() { return true; } + /** + * Sets the underlying {@link #exporter}. + * If an active {@link SpanExporter} was previously set, it will be shut down. + * + * @param exporter + */ + public void setExporter(T exporter) { + lock.writeLock().lock(); + try { + this.exporter.flush(); + CompletableResultCode resultCode = this.exporter.shutdown(); + // TODO: do we need to wait for shutdown to complete? + } catch (Exception e) { + log.error("failed to shut down or flush exporter " + exporter.getClass() + " before setting new exporter", e); + } + this.exporter = exporter; + lock.writeLock().unlock(); + } + + /** + * Registers a new service handlers that is used to export {@link SpanData} for sampled {@link io.opentelemetry.api.trace.Span}s + * + * @param name the name of the service handler. Must be unique for each service. + * @param serviceHandler the service handler that is called for each ended sampled {@link io.opentelemetry.api.trace.Span} + */ + public void registerHandler(String name, Handler serviceHandler) { + serviceHandlers.put(name, serviceHandler); + } + + /** + * Unregisters the service handler with the provided name. + * + * @param name the name of the service handler that will be unregistered + */ + public void unregisterHandler(String name) { + serviceHandlers.remove(name); + } + + /** + * Mirror of {@link io.opencensus.trace.export.SpanExporter.Handler}. + * An abstract class that allows different tracing services to export recorded data for sampled spans in their own format. + */ + public abstract class Handler { + + /** + * Exports a list of sampled {@link io.opentelemetry.api.trace.Span}s using the immutable representation {@link SpanData}. + * + * @param spanDataList a list of {@link SpanData} objects to be exported. + */ + public abstract void export(Collection spanDataList); + + } + } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java index 9eaebb6ff4..302f36bb32 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java @@ -4,16 +4,23 @@ import io.opentelemetry.sdk.trace.ReadWriteSpan; import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import io.opentelemetry.sdk.trace.export.SpanExporter; -import lombok.Data; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * A custom {@link SpanProcessor} wrapper that can be dynamically enabled or disabled. */ -@Data public class DynamicallyActivatableSpanProcessor implements SpanProcessor { + @Getter + @Setter(AccessLevel.PRIVATE) private boolean enabled; /** @@ -21,10 +28,19 @@ public class DynamicallyActivatableSpanProcessor implements SpanProcessor { */ private SpanProcessor spanProcessor; + private Map spanProcessors = new ConcurrentHashMap<>(); + private DynamicallyActivatableSpanProcessor(SpanProcessor spanProcessor) { this.spanProcessor = spanProcessor; } + /** + * Creates a new {@link DynamicallyActivatableSpanProcessor} with the given {@link SpanProcessor} as the implementation to process spans. + * + * @param spanProcessor The {@link SpanProcessor} that will be used to process the spans + * + * @return A new {@link DynamicallyActivatableSpanProcessor} with the given {@link SpanProcessor} as the implementation to process spans. + */ public static DynamicallyActivatableSpanProcessor create(SpanProcessor spanProcessor) { return new DynamicallyActivatableSpanProcessor(spanProcessor); } @@ -33,10 +49,20 @@ public static DynamicallyActivatableSpanProcessor createSimpleSpanProcessor(Span return new DynamicallyActivatableSpanProcessor(SimpleSpanProcessor.create(spanExporter)); } + public static DynamicallyActivatableSpanProcessor createBatchSpanProcessor(SpanExporter... spanExporters) { + return new DynamicallyActivatableSpanProcessor(BatchSpanProcessor.builder(SpanExporter.composite(spanExporters)) + .build()); + } + @Override public void onStart(Context parentContext, ReadWriteSpan span) { // if enabled, call the real processor's onStart if (isEnabled()) { + if (null != spanProcessors) { + for (SpanProcessor spanProc : spanProcessors.values()) { + spanProc.onStart(parentContext, span); + } + } spanProcessor.onStart(parentContext, span); } // otherwise, do nothing @@ -44,13 +70,18 @@ public void onStart(Context parentContext, ReadWriteSpan span) { @Override public boolean isStartRequired() { - return spanProcessor.isStartRequired(); + return isEnabled() ? spanProcessor.isStartRequired() : false; } @Override public void onEnd(ReadableSpan span) { // if enabled, call the real processor's onEnd if (isEnabled()) { + if (null != spanProcessors) { + for (SpanProcessor spanProc : spanProcessors.values()) { + spanProc.onEnd(span); + } + } spanProcessor.onEnd(span); } // otherwise, do nothing @@ -58,6 +89,24 @@ public void onEnd(ReadableSpan span) { @Override public boolean isEndRequired() { - return spanProcessor.isEndRequired(); + return isEnabled() ? spanProcessor.isEndRequired() : false; + } + + public boolean doEnable() { + setEnabled(true); + return true; + } + + public boolean doDisable() { + setEnabled(false); + return true; + } + + public void registerService(String registerName, SpanProcessor spanProcessor) { + spanProcessors.put(registerName, spanProcessor); + } + + public void unregisterService(String registerName) { + spanProcessors.remove(registerName); } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java new file mode 100644 index 0000000000..1c001e2723 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java @@ -0,0 +1,35 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.sdk.trace.SpanProcessor; +import lombok.Getter; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.metrics.PrometheusExporterSettings; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; + +/** + * Base class for trace export services that can be dynamically enabled and disabled based on the {@link InspectitConfig}. + * This class extends {@link DynamicallyActivatableService} which handles the waiting for changes in the configuration. + */ +public abstract class DynamicallyActivatableTraceExporterService extends DynamicallyActivatableService { + + /** + * Gets the {@link SpanProcessor} used to process {@link io.opentelemetry.api.trace.Span} + * + * @return The {@link SpanProcessor} used to process {@link io.opentelemetry.api.trace.Span} + */ + public abstract SpanProcessor getSpanProcessor(); + + /** + * Constructor. + * + * @param configDependencies The list of configuration properties in camelCase this service depends on. + * For example "exporters.metrics.prometheus" specifies a dependency + * to {@link PrometheusExporterSettings} + * and all its children. + */ + public DynamicallyActivatableTraceExporterService(String... configDependencies) { + super(configDependencies); + } + + +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java index c623dd7859..b012bfda3d 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java @@ -1,26 +1,45 @@ package rocks.inspectit.ocelot.core.exporter; +import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter; +import io.opentelemetry.exporter.jaeger.thrift.JaegerThriftSpanExporter; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerExporterSettings; -import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; import javax.validation.Valid; /** - * Service for the Jaeger OpenCensus exporter. + * Service for the Jaeger OpenTelemetry exporter. * Can be dynamically started and stopped using the exporters.trace.jaeger.enabled configuration. */ @Component @Slf4j -public class JaegerExporterService extends DynamicallyActivatableService { +public class JaegerExporterService extends DynamicallyActivatableTraceExporterService { + + private JaegerGrpcSpanExporter grpcSpanExporter; + + private JaegerThriftSpanExporter thriftSpanExporter; + + private DynamicallyActivatableSpanExporter spanExporter; + + @Getter + private SpanProcessor spanProcessor; public JaegerExporterService() { super("exporters.tracing.jaeger", "tracing.enabled"); } + @Override + protected void init() { + super.init(); + } + @Override protected boolean checkEnabledForConfig(InspectitConfig conf) { @Valid JaegerExporterSettings jaeger = conf.getExporters().getTracing().getJaeger(); @@ -39,12 +58,25 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { protected boolean doEnable(InspectitConfig configuration) { try { JaegerExporterSettings settings = configuration.getExporters().getTracing().getJaeger(); - log.info("Starting Jaeger Exporter with url '{}'", settings.getUrl()); - // TODO re-implement with OTel - /* - JaegerTraceExporter.createAndRegister( - JaegerExporterConfiguration.builder().setThriftEndpoint(settings.getUrl()).setServiceName(settings.getServiceName()).build()); - */ + + log.info("Starting Jaeger Exporter with url '{}' (grpc '{}')", settings.getUrl(), settings.getGrpc()); + + // TODO: use getUrl() or getGRPC()? + /*grpcSpanExporter = JaegerGrpcSpanExporter.builder() + .setEndpoint(settings.getGrpc() == null ? settings.getUrl() : settings.getGrpc()) + .build();*/ + + // create span exporter + thriftSpanExporter = JaegerThriftSpanExporter.builder().setEndpoint(settings.getUrl()).build(); + spanExporter = DynamicallyActivatableSpanExporter.create(thriftSpanExporter); + spanExporter.doEnable(); + + // create span processor + spanProcessor = BatchSpanProcessor.builder(spanExporter).build(); + + // register + openTelemetryController.registerTraceExporterService(this); + return true; } catch (Throwable t) { log.error("Error creating Jaeger exporter", t); @@ -57,12 +89,18 @@ protected boolean doDisable() { log.info("Stopping Jaeger Exporter"); try { // TODO: reimplement with OTel - /* - JaegerTraceExporter.unregister(); - */ + if (null != spanExporter) { + spanExporter.doDisable(); + } + if (null != spanProcessor) { + spanProcessor.shutdown(); + spanProcessor = null; + } + openTelemetryController.unregisterTraceExporterService(this); } catch (Throwable t) { log.error("Error disabling Jaeger exporter", t); } return true; } + } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java index 41157ecc52..f41ad58db6 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java @@ -1,18 +1,14 @@ package rocks.inspectit.ocelot.core.exporter; -import io.opentelemetry.api.common.Attributes; import io.opentelemetry.exporter.logging.LoggingMetricExporter; -import io.opentelemetry.opencensusshim.metrics.OpenCensusMetrics; -import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.config.model.exporters.metrics.LoggingMetricsExporterSettings; -import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; import javax.validation.Valid; @@ -21,12 +17,8 @@ */ @Component @Slf4j -public class LoggingMetricExporterService extends DynamicallyActivatableService { +public class LoggingMetricExporterService extends DynamicallyActivatableMetricsExporterService { - /** - * The {@link SdkMeterProvider} - */ - private SdkMeterProvider meterProvider; /** * The {@link DynamicallyActivatableMetricExporter} for exporting metrics to the log @@ -38,10 +30,8 @@ public class LoggingMetricExporterService extends DynamicallyActivatableService */ private PeriodicMetricReaderBuilder metricReader; - /** - * The service name {@link Resource} - */ - private Resource serviceNameResource; + @Getter + MetricReaderFactory metricReaderFactory; public LoggingMetricExporterService() { super("exporters.metrics.logging", "metrics.enabled"); @@ -53,13 +43,6 @@ protected void init() { // create new metric exporter metricExporter = DynamicallyActivatableMetricExporter.createLoggingExporter(); - - // close the tracer provider when the JVM is shutting down - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - if (null != meterProvider) { - meterProvider.shutdown(); - } - })); } @Override @@ -74,18 +57,19 @@ protected boolean doEnable(InspectitConfig configuration) { try { // build and register the MeterProvider metricReader = PeriodicMetricReader.builder(metricExporter).setInterval(logging.getExportInterval()); - - meterProvider = SdkMeterProvider.builder() - .setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, configuration.getServiceName()))) - .registerMetricReader(OpenCensusMetrics.attachTo(metricReader.newMetricReaderFactory())) - .buildAndRegisterGlobal(); - - // enable the metric exporter - metricExporter.doEnable(); - log.info("Starting LoggingMetricsExporter"); - return true; + metricReaderFactory = metricReader.newMetricReaderFactory(); + boolean success = openTelemetryController.registerMetricExporterService(this); + if (success) { + // enable the metric exporter + metricExporter.doEnable(); + log.info("Starting {}", getClass().getSimpleName()); + } else { + log.error("Failed to register {} at {}!", getClass().getSimpleName(), openTelemetryController.getClass() + .getSimpleName()); + } + return success; } catch (Exception e) { - log.error("Failed to start LoggingMetricExporter", e); + log.error("Failed to start " + getClass().getSimpleName(), e); return false; } } @@ -93,14 +77,9 @@ protected boolean doEnable(InspectitConfig configuration) { @Override protected boolean doDisable() { try { - if (null != meterProvider) { - // flush all metrics before disabling them - meterProvider.forceFlush(); - meterProvider.shutdown(); - meterProvider = null; - } metricExporter.doDisable(); log.info("Stopping LoggingMetricExporter"); + openTelemetryController.unregisterMetricExporterService(this); return true; } catch (Exception e) { log.error("Failed to stop LoggingMetricExporter", e); diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java index 76c24f8d78..179c6784b3 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java @@ -1,65 +1,33 @@ package rocks.inspectit.ocelot.core.exporter; -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; -import io.opentelemetry.context.propagation.ContextPropagators; -import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.exporter.logging.LoggingSpanExporter; -import io.opentelemetry.extension.trace.propagation.JaegerPropagator; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; -import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.config.model.exporters.trace.LoggingTraceExporterSettings; -import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; -import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; +import rocks.inspectit.ocelot.core.opentelemetry.OpenTelemetryControllerImpl; import javax.validation.Valid; /** - * Service for the {@link io.opentelemetry.exporter.logging.LoggingMetricExporter} + * Service for the {@link io.opentelemetry.exporter.logging.LoggingSpanExporter} */ @Component @Slf4j -public class LoggingTraceExporterService extends DynamicallyActivatableService { - - /** - * The {@link OpenTelemetry} - */ - private OpenTelemetry openTelemetry; - - /** - * The {@link SdkTracerProvider} - */ - private SdkTracerProvider tracerProvider; - - /** - * The {@link DynamicallyActivatableSampler} for the {@link #tracerProvider} - */ - private DynamicallyActivatableSampler sampler; - - /** - * The {@link SpanProcessor} of the {@link #spanExporter - */ - private SpanProcessor simpleSpanProcessor; +public class LoggingTraceExporterService extends DynamicallyActivatableTraceExporterService { /** * The {@link DynamicallyActivatableSpanExporter< LoggingSpanExporter >} for exporting the spans to the log */ private DynamicallyActivatableSpanExporter spanExporter; - /** - * The service name {@link Resource} - */ - private Resource serviceNameResource; + @Getter + private SpanProcessor spanProcessor; + public LoggingTraceExporterService() { super("exporters.tracing.logging", "tracing.enabled"); @@ -71,13 +39,6 @@ protected void init() { // create span exporter and span processors spanExporter = DynamicallyActivatableSpanExporter.createLoggingSpanExporter(); - - // close the tracer provider when the JVM is shutting down - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - if (null != tracerProvider) { - tracerProvider.shutdown(); - } - })); } @Override @@ -92,47 +53,23 @@ protected boolean doEnable(InspectitConfig conf) { LoggingTraceExporterSettings logging = conf.getExporters().getTracing().getLogging(); try { - // reset GlobalOpenTelemetry - GlobalOpenTelemetry.resetForTest(); - // create span processors // SpanProcessors are also shut down when the corresponding TracerProvider is shut down. Thus, we need to create the SpanProcessors each time - simpleSpanProcessor = SimpleSpanProcessor.create(spanExporter); - - // create sampler - sampler = DynamicallyActivatableSampler.createRatio(env.getCurrentConfig() - .getTracing() - .getSampleProbability()); - - // create Resource for the service name - serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, logging.getServiceName())); - - // set up tracer provider - tracerProvider = SdkTracerProvider.builder() - .addSpanProcessor(simpleSpanProcessor) - .setSampler(sampler) - .setResource(serviceNameResource) - .build(); - - // build and register OTel - openTelemetry = OpenTelemetrySdk.builder() - .setTracerProvider(tracerProvider) - .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance()))) - // TODO: do I also need the W3CBaggagePropagator? - // W3CBaggagePropagator.getInstance() - .buildAndRegisterGlobal(); - - // update OC tracer - OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); + spanProcessor = SimpleSpanProcessor.create(spanExporter); // enable span exporter spanExporter.doEnable(); - log.info("Starting TraceLoggingSpanExporter"); - - return true; + boolean success = openTelemetryController.registerTraceExporterService(this); + if (success) { + log.info("Starting {}", getClass().getSimpleName()); + } else { + log.error("Failed to register {} at {}!", getClass().getSimpleName(), openTelemetryController.getClass() + .getSimpleName()); + } + return success; } catch (Exception e) { - log.error("Failed to start TraceLoggingExporter", e); + log.error("Failed to start " + getClass().getSimpleName(), e); return false; } } @@ -141,12 +78,15 @@ protected boolean doEnable(InspectitConfig conf) { protected boolean doDisable() { try { // disable the span exporter - if (null != spanExporter && null != tracerProvider) { + if (null != spanExporter) { spanExporter.doDisable(); - tracerProvider.forceFlush(); - tracerProvider.shutdown(); - tracerProvider = null; } + // shut down the span processor + if (null != spanProcessor) { + spanProcessor.shutdown(); + spanProcessor = null; + } + openTelemetryController.unregisterTraceExporterService(this); log.info("Stopping TraceLoggingSpanExporter"); return true; } catch (Exception e) { diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java new file mode 100644 index 0000000000..20bb94cb78 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java @@ -0,0 +1,87 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.trace.OtlpTraceExporterSettings; + +import javax.validation.Valid; + +/** + * Service for {@link io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter}. + * Can be dynamically started and stopped using the exporters.trace.otlp.enabled configuration + */ +@Component +@Slf4j +public class OtlpTraceExporterService extends DynamicallyActivatableTraceExporterService { + + @Getter + private SpanProcessor spanProcessor; + + private OtlpGrpcSpanExporter otlpSpanExporter; + + private DynamicallyActivatableSpanExporter spanExporter; + + public OtlpTraceExporterService() { + super("exporters.tracing.otlp", "tracing.enabled"); + } + + @Override + protected boolean checkEnabledForConfig(InspectitConfig configuration) { + @Valid OtlpTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlp(); + return configuration.getTracing().isEnabled() && !StringUtils.isEmpty(otlp.getUrl()) && otlp.isEnabled(); + } + + @Override + protected boolean doEnable(InspectitConfig configuration) { + try { + OtlpTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlp(); + log.info("Starting OTLP Trace Exporter with endpoint {}", otlp.getUrl()); + + // create span exporter + otlpSpanExporter = OtlpGrpcSpanExporter.builder().setEndpoint(otlp.getUrl()).build(); + spanExporter = DynamicallyActivatableSpanExporter.create(otlpSpanExporter); + + // create span processor + spanProcessor = BatchSpanProcessor.builder(spanExporter).build(); + + // register service + openTelemetryController.registerTraceExporterService(this); + return true; + } catch (Throwable t) { + log.error("Error creating OTLP trace exporter", t); + return false; + } + } + + @Override + protected boolean doDisable() { + + log.info("Stopping OTLP trace exporter"); + try { + + // disable span exporter + if (null != spanExporter) { + spanExporter.doDisable(); + } + + // shut down span processor + if (null != spanProcessor) { + spanProcessor.shutdown(); + spanProcessor = null; + } + + // unregister service + openTelemetryController.unregisterTraceExporterService(this); + } + catch (Throwable t){ + log.error("Error disabling OTLP trace exporter", t); + } + return true; + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java index 62d796cc2c..8b977a7c5c 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java @@ -1,24 +1,26 @@ package rocks.inspectit.ocelot.core.exporter; - -import io.prometheus.client.exporter.HTTPServer; +import io.opentelemetry.exporter.prometheus.PrometheusHttpServer; +import io.opentelemetry.exporter.prometheus.PrometheusHttpServerBuilder; +import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; -import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; - -import static io.prometheus.client.CollectorRegistry.defaultRegistry; /** - * Service for the Prometheus OpenCensus exporter. + * Service for the Prometheus OpenTelemetry exporter. * Can be dynamically started and stopped using the exporters.metrics.prometheus.enabled configuration. */ @Component @Slf4j -public class PrometheusExporterService extends DynamicallyActivatableService { +public class PrometheusExporterService extends DynamicallyActivatableMetricsExporterService { + + private PrometheusHttpServerBuilder prometheusHttpServerBuilder; - private HTTPServer prometheusClient = null; + @Getter + private MetricReaderFactory metricReaderFactory; public PrometheusExporterService() { super("exporters.metrics.prometheus", "metrics.enabled"); @@ -32,16 +34,16 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { @Override protected boolean doEnable(InspectitConfig configuration) { val config = configuration.getExporters().getMetrics().getPrometheus(); + try { String host = config.getHost(); int port = config.getPort(); log.info("Starting Prometheus Exporter on {}:{}", host, port); - // TODO: implement OTel PrometheusStatsCollector - // PrometheusStatsCollector.createAndRegister(PrometheusStatsConfiguration.builder().setRegistry(defaultRegistry).build()); - prometheusClient = new HTTPServer(host, port, true); + prometheusHttpServerBuilder = PrometheusHttpServer.builder().setHost(host).setPort(port); + metricReaderFactory = prometheusHttpServerBuilder.newMetricReaderFactory(); + openTelemetryController.registerMetricExporterService(this); } catch (Exception e) { log.error("Error Starting Prometheus HTTP Endpoint!", e); - defaultRegistry.clear(); return false; } return true; @@ -50,10 +52,8 @@ protected boolean doEnable(InspectitConfig configuration) { @Override protected boolean doDisable() { log.info("Stopping Prometheus Exporter"); - if (prometheusClient != null) { - prometheusClient.stop(); - defaultRegistry.clear(); - } + openTelemetryController.unregisterMetricExporterService(this); return true; } + } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/SpanExporterImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/SpanExporterImpl.java new file mode 100644 index 0000000000..35b6261655 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/SpanExporterImpl.java @@ -0,0 +1,54 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +public class SpanExporterImpl implements SpanExporter { + + private Map spanExporters = new ConcurrentHashMap<>(); + + private Object lock = new Object(); + + @Override + public CompletableResultCode export(Collection spans) { + CompletableResultCode resultCode; + synchronized (lock) { + resultCode = execute(spanExporters.values(), (spanExporter) -> spanExporter.export(spans)); + } + return resultCode; + } + + @Override + public CompletableResultCode flush() { + CompletableResultCode resultCode; + synchronized (lock) { + resultCode = execute(spanExporters.values(), (spanExporter) -> spanExporter.flush()); + } + return resultCode; + } + + @Override + public CompletableResultCode shutdown() { + CompletableResultCode resultCode; + synchronized (lock) { + resultCode = execute(spanExporters.values(), (spanExporter) -> spanExporter.shutdown()); + } + return resultCode; + } + + private static CompletableResultCode execute(Collection spanExporters, Function spanExporterRunHandler) { + List resultCodes = new ArrayList<>(spanExporters.size()); + for (SpanExporter spanExporter : spanExporters) { + resultCodes.add(spanExporterRunHandler.apply(spanExporter)); + } + return CompletableResultCode.ofAll(resultCodes); + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java index b1dff825d2..b2d8a997bc 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java @@ -1,22 +1,32 @@ package rocks.inspectit.ocelot.core.exporter; - +import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.config.model.exporters.trace.ZipkinExporterSettings; -import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; import javax.validation.Valid; /** - * Service for the ZipKin OpenCensus exporter. + * Service for the {@link ZipkinSpanExporter ZipKin OpenTelemetry exporter}. * Can be dynamically started and stopped using the exporters.trace.zipkin.enabled configuration. */ @Component @Slf4j -public class ZipkinExporterService extends DynamicallyActivatableService { +public class ZipkinExporterService extends DynamicallyActivatableTraceExporterService { + + private ZipkinSpanExporter zipkinSpanExporter; + + private DynamicallyActivatableSpanExporter spanExporter; + + @Getter + private SpanProcessor spanProcessor; public ZipkinExporterService() { super("exporters.tracing.zipkin", "tracing.enabled"); @@ -33,11 +43,17 @@ protected boolean doEnable(InspectitConfig configuration) { try { ZipkinExporterSettings settings = configuration.getExporters().getTracing().getZipkin(); log.info("Starting Zipkin Exporter with url '{}'", settings.getUrl()); - // TODO: implement OTel equivalent - /* - ZipkinTraceExporter.createAndRegister( - ZipkinExporterConfiguration.builder().setV2Url(settings.getUrl()).setServiceName(settings.getServiceName()).build()); - */ + + // create span exporter + zipkinSpanExporter = ZipkinSpanExporter.builder().setEndpoint(settings.getUrl()).build(); + spanExporter = DynamicallyActivatableSpanExporter.create(zipkinSpanExporter); + spanExporter.doEnable(); + + // create span processor + spanProcessor = BatchSpanProcessor.builder(spanExporter).build(); + + // register + openTelemetryController.registerTraceExporterService(this); return true; } catch (Throwable t) { log.error("Error creating Zipkin exporter", t); @@ -49,13 +65,19 @@ protected boolean doEnable(InspectitConfig configuration) { protected boolean doDisable() { log.info("Stopping Zipkin Exporter"); try { - // TODO: implement OTel equivalent - /* - ZipkinTraceExporter.unregister(); - */ + if (null != spanExporter) { + spanExporter.doDisable(); + } + // shut down the span processor + if (null != spanProcessor) { + spanProcessor.shutdown(); + spanProcessor = null; + } + openTelemetryController.unregisterTraceExporterService(this); } catch (Throwable t) { log.error("Error disabling Zipkin exporter", t); } return true; } + } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/CustomMetricReaderFactory.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/CustomMetricReaderFactory.java new file mode 100644 index 0000000000..474f6b30e6 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/CustomMetricReaderFactory.java @@ -0,0 +1,44 @@ +package rocks.inspectit.ocelot.core.opentelemetry; + +import io.opentelemetry.sdk.metrics.export.MetricProducer; +import io.opentelemetry.sdk.metrics.export.MetricReader; +import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +public class CustomMetricReaderFactory implements MetricReaderFactory { + + private Map readerFactories = new ConcurrentHashMap<>(); + + @Override + public MetricReader apply(MetricProducer producer) { + return null; + } + + public boolean registerMetricReaderFactory(String registerName, MetricReaderFactory factory) { + // TODO: support immediate replacement? or do we need to unregister it before? + if (null != readerFactories.put(registerName, factory)) { + log.error("A MetricReaderFactory with the name '{}' was already registered.", registerName); + return false; + } else { + log.info("Successfully registered the '{}' MetricReaderFactory ({})", registerName, factory.getClass() + .getName()); + return true; + } + } + + public boolean unregisterMetricReaderFactory(String registerName) { + MetricReaderFactory metricReaderFactory = readerFactories.remove(registerName); + if (null == metricReaderFactory) { + log.error("Could not unregister '{}' as no such MetricReaderFactory was registered", registerName); + return false; + } else { + log.info("Successfully unregistered the '{}' MetricReaderFactory ({}}", registerName, metricReaderFactory.getClass() + .getName()); + return true; + } + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java new file mode 100644 index 0000000000..19b51e63f9 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java @@ -0,0 +1,77 @@ +package rocks.inspectit.ocelot.core.opentelemetry; + +import io.opentelemetry.api.metrics.GlobalMeterProvider; +import io.opentelemetry.api.metrics.MeterBuilder; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import lombok.Builder; +import lombok.extern.slf4j.Slf4j; +import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; + +/** + * Custom implementation of {@link MeterProvider} thats wraps {@link SdkMeterProvider}. + * This wrapper is used to change {@link #meterProvider} without resetting {@link io.opentelemetry.api.metrics.GlobalMeterProvider#set(MeterProvider)}. + * For use, create the {@link MeterProviderImpl} and set it once to {@link io.opentelemetry.api.metrics.GlobalMeterProvider#set(MeterProvider)}. If the {@link SdkMeterProvider} changes, simply + */ +@Slf4j +@Builder +public class MeterProviderImpl implements MeterProvider { + + private SdkMeterProvider meterProvider; + + @Builder.Default + private Object lock = new Object(); + + /** + * Registers the {@link SdkMeterProvider}. If an instance of {@link SdkMeterProvider} was already registered, it is {@link SdkMeterProvider#forceFlush() flushed} and {@link SdkMeterProvider#shutdown() shutdown} before the new {@link SdkMeterProvider} is set to {@link #meterProvider} + * + * @param meterProvider + */ + public void set(SdkMeterProvider meterProvider) { + synchronized (lock) { + // shut down previous meterProvider if set + if (null != this.meterProvider) { + log.info("Set new SdkMeterProvider. Shut down previous ({})", meterProvider); + OpenTelemetryUtils.stopMeterProvider(this.meterProvider, true); + } + // set SdkMeterProvider + this.meterProvider = meterProvider; + } + } + + /** + * Gets the currently registered {@link SdkMeterProvider} + * + * @return The currently registered {@link SdkMeterProvider} + */ + public SdkMeterProvider get() { + return meterProvider; + } + + /** + * Shuts down the currently registered {@link #meterProvider} + * + * @return + */ + public synchronized CompletableResultCode shutdown() { + CompletableResultCode result; + synchronized (lock) { + result = OpenTelemetryUtils.stopMeterProvider(meterProvider, true); + } + return result; + } + + /** + * Registers {@link SdkMeterProvider this} to {@link GlobalMeterProvider#set(MeterProvider)} + */ + public MeterProvider registerGlobal() { + GlobalMeterProvider.set(this.meterProvider); + return this; + } + + @Override + public MeterBuilder meterBuilder(String instrumentationName) { + return this.meterProvider.meterBuilder(instrumentationName); + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java new file mode 100644 index 0000000000..4cec91b803 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -0,0 +1,526 @@ +package rocks.inspectit.ocelot.core.opentelemetry; + +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.GlobalMeterProvider; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.extension.trace.propagation.B3Propagator; +import io.opentelemetry.extension.trace.propagation.JaegerPropagator; +import io.opentelemetry.opencensusshim.metrics.OpenCensusMetrics; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import rocks.inspectit.ocelot.bootstrap.Instances; +import rocks.inspectit.ocelot.bootstrap.opentelemetry.IOpenTelemetryController; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent; +import rocks.inspectit.ocelot.core.config.InspectitEnvironment; +import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService; +import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableSpanExporter; +import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableTraceExporterService; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; +import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; +import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; + +import javax.annotation.PostConstruct; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * The implementation of {@link IOpenTelemetryController}. The {@link OpenTelemetryControllerImpl} configures {@link GlobalOpenTelemetry} (tracing) and {@link GlobalMeterProvider} (metrics). + *

+ * The hierarchy of {@link OpenTelemetrySdk} is as follows. All fields are private final and thus can only be changed via reflection + *

+ * The {@link OpenTelemetrySdk} contains the {@link SdkTracerProvider}, i.e., {@link OpenTelemetrySdk#getSdkTracerProvider()}. + * The {@link SdkTracerProvider} contains the {@link io.opentelemetry.sdk.trace.TracerSharedState}, i.e., {@link SdkTracerProvider#sharedState}. + * The {@link io.opentelemetry.sdk.trace.TracerSharedState} contains {@link io.opentelemetry.sdk.trace.TracerSharedState#activeSpanProcessor}, which can be a list of {@link SpanProcessor} (for example {@link SimpleSpanProcessor} or {@link io.opentelemetry.sdk.trace.export.BatchSpanProcessor}. + *

+ * The {@link SimpleSpanProcessor} contains the {@link SimpleSpanProcessor#spanExporter}, i.e., {@link io.opentelemetry.sdk.trace.export.SpanExporte}. + * The {@link io.opentelemetry.sdk.trace.export.BatchSpanProcessor} contains the {@link io.opentelemetry.sdk.trace.export.BatchSpanProcessor#worker}, which then contains the {@link io.opentelemetry.sdk.trace.export.BatchSpanProcessor.Worker#spanExporter} + *

+ * The hierarchy of {@link io.opentelemetry.sdk.metrics.SdkMeterProvider} is as follows. All fields are private final and thus can only be changed via reflection. + *

+ * The hierarchy of {@link io.opentelemetry.sdk.metrics.SdkMeterProvider} is as follows. All fields are private final and thus can only be changed via reflection. + *

+ * The {@link io.opentelemetry.sdk.metrics.SdkMeterProvider} contains {@link io.opentelemetry.sdk.metrics.SdkMeterProvider#sharedState} and {@link io.opentelemetry.sdk.metrics.SdkMeterProvider#collectionInfoMap}. + * The {@link io.opentelemetry.sdk.metrics.internal.state.AutoValue_MeterProviderSharedState} has nothing of interest. + * The {@link io.opentelemetry.sdk.metrics.internal.export.AutoValue_CollectionInfo} contains the {@link io.opentelemetry.sdk.metrics.internal.export.AutoValue_CollectionInfo#reader}, i.e., {@link io.opencensus.exporter.metrics.util.MetricReader}. + * The {@link io.opentelemetry.sdk.metrics.export.MetricReaderFactory} can be implemented as {@link io.opentelemetry.sdk.metrics.export.PeriodicMetricReader}. + * The {@link io.opentelemetry.sdk.metrics.export.PeriodicMetricReader} contains {@link io.opentelemetry.sdk.metrics.export.PeriodicMetricReader#exporter}, e.g., {@link io.opentelemetry.exporter.logging.LoggingMetricExporter}, and the {@link io.opentelemetry.sdk.metrics.export.PeriodicMetricReader#scheduledFuture}. + * The {@link java.util.concurrent.ScheduledFuture} + */ +@Slf4j +public class OpenTelemetryControllerImpl implements IOpenTelemetryController { + + public static final String BEAN_NAME = "openTelemetryController"; + + @Getter + private boolean enabled = false; + + /** + * Whether something in {@link rocks.inspectit.ocelot.config.model.tracing.TracingSettings} of the {@link InspectitConfig} changed + */ + private boolean tracingSettingsChanged = false; + + /** + * Whether something in {@link rocks.inspectit.ocelot.config.model.metrics.MetricsSettings} of the {@link InspectitConfig} changed + */ + private boolean metricSettingsChanged = false; + + /** + * whether {@link GlobalOpenTelemetry} and {@link GlobalMeterProvider} have successfully been configured. + */ + @Getter + // TODO: make sure that configured is set accordingly when OTEL is being reconfigured (false while configuring, true after success) + private boolean configured = false; + + /** + * Whether the {@link OpenTelemetryControllerImpl} is currently configuring and starting + */ + private AtomicBoolean isConfiguring = new AtomicBoolean(false); + + /** + * Returns whether the {@link OpenTelemetryControllerImpl} is currently (re-)configuring tracing and metrics + * + * @return Whether the {@link OpenTelemetryControllerImpl} is currently (re-)configuring tracing and metrics + */ + public boolean isConfiguring() { + return isConfiguring.get(); + } + + /** + * The set of registered {@link DynamicallyActivatableTraceExporterService}. + */ + private Set registeredTraceExportServices; + + /** + * The set of registered {@link DynamicallyActivatableMetricsExporterService}. + */ + private Set registeredMetricExporterServices; + + private Resource serviceNameResource; + + /** + * The custom {@link OpenTelemetryImpl} + */ + private OpenTelemetryImpl openTelemetry; + + private MeterProviderImpl meterProvider; + + @Autowired + InspectitEnvironment env; + + private Sampler sampler; + + @PostConstruct + void init() { + log.info("INIT at timestamp {}", System.currentTimeMillis()); + + registeredTraceExportServices = new LinkedHashSet<>(); + registeredMetricExporterServices = new LinkedHashSet<>(); + + serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, env.getCurrentConfig() + .getServiceName())); + + // close the tracer provider when the JVM is shutting down + Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); + + enabled = true; + + Instances.openTelemetryController = this; + } + + @EventListener(ContextRefreshedEvent.class) + @Order(Ordered.LOWEST_PRECEDENCE) + synchronized private void startAtStartup(ContextRefreshedEvent event) { + // start and configure OTEL at when the ApplicationContext gets initialized + start(); + } + + /** + * Configures and registers {@link io.opentelemetry.api.OpenTelemetry}, triggered by the {@link rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent} triggered + * For tracing, the {@link SdkTracerProvider} is reconfigured and updated in the {@link GlobalOpenTelemetry}. + * For metrics, the {@link SdkMeterProvider} is reconfigured and updated in the {@link GlobalMeterProvider} + * + * @return + */ + @EventListener(InspectitConfigChangedEvent.class) + @Order(Ordered.LOWEST_PRECEDENCE) + // make sure this is called after the individual services have (un)-registered + synchronized private boolean configureOpenTelemetry() { + log.info("configureOpenTelemetry at timestamp {}", System.currentTimeMillis()); + boolean success = false; + if (!isConfiguring.compareAndSet(false, true)) { + log.info("Multiple configure calls"); + return true; + } + if (enabled) { + + InspectitConfig configuration = env.getCurrentConfig(); + + // TODO: somehow compute whether anything has changed in tracing or metrics. If no changes happened, we do not need to reconfigure tracing and metrics! + + // set serviceName + serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, configuration.getServiceName())); + + // configure tracing if not configured or when tracing settings changed + boolean successConfigureTracing = !(tracingSettingsChanged || !configured) ? true : configureTracing(configuration); + + // configure meter provider (metrics) if not configured or when metrics settings changed + boolean successConfigureMeterProvider = !(metricSettingsChanged || !configured) ? true : configureMeterProvider(configuration); + + if (successConfigureTracing && successConfigureMeterProvider) { + log.info("Successfully configured OpenTelemetry with TracerProvider and MeterProvider"); + } else { + log.error("Failed to configure OpenTelemetry. Please scan the logs for detailed failure messages."); + } + success = successConfigureTracing && successConfigureMeterProvider; + } + + isConfiguring.set(false); + // reset changed variables + tracingSettingsChanged = false; + metricSettingsChanged = false; + return success; + + } + + /** + * Shuts down the {@link SdkTracerProvider} set for {@link GlobalOpenTelemetry} and the {@link SdkMeterProvider} set for {@link GlobalMeterProvider} + */ + @Override + synchronized public void shutdown() { + long start = System.nanoTime(); + + if (null != openTelemetry) { + openTelemetry.shutdown(); + } + if (null != meterProvider) { + long startMeterProviderShutdown = System.nanoTime(); + CompletableResultCode shutdownResult = meterProvider.shutdown(); + log.info("time to shut down {}: {} ms", meterProvider, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startMeterProviderShutdown)); + } + GlobalMeterProvider.set(null); + GlobalOpenTelemetry.resetForTest(); + configured = false; + enabled = false; + + // set all OTEL related fields to null + openTelemetry = null; + meterProvider = null; + serviceNameResource = null; + sampler = null; + + log.info("Shut down {}. The shutdown process took {} ms", getClass().getSimpleName(), TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)); + } + + @Override + synchronized public boolean start() { + enabled = true; + // if OTEL has not been configured (since last shutdown), configure it + if (!configured) { + return configured = configureOpenTelemetry(); + } else { + return true; + } + } + + @Override + synchronized public void notifyTracingSettingsChanged() { + tracingSettingsChanged = true; + } + + @Override + synchronized public void notifyMetricsSettingsChanged() { + metricSettingsChanged = true; + } + + public static void configureAndRegisterDefault() { + log.info("Configure and register default OpenTelemetry"); + + // set up tracer provider + SdkTracerProvider tracerProvider = SdkTracerProvider.builder() + .addSpanProcessor(SimpleSpanProcessor.create(new LoggingSpanExporter())) + .setSampler(Sampler.alwaysOn()) + .build(); + + // build and register OTel + OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance()))) + // W3CBaggagePropagator.getInstance() + .buildAndRegisterGlobal(); + } + + /** + * Registers a new {@link DynamicallyActivatableService} + * + * @param service + * + * @return + */ + // TODO: make accessible? discuss whether it is better to have this kind of catch-all method or rather individual register methods (see below) + private boolean registerExporterService(DynamicallyActivatableService service) { + if (service instanceof DynamicallyActivatableTraceExporterService) { + return registerTraceExporterService((DynamicallyActivatableTraceExporterService) service); + } else if (service instanceof DynamicallyActivatableMetricsExporterService) { + return registerMetricExporterService((DynamicallyActivatableMetricsExporterService) service); + } else { + log.error("Cannot register service {}. The class is not supported. Currently supported classes are {} and {}", service.getName(), DynamicallyActivatableTraceExporterService.class, DynamicallyActivatableMetricsExporterService.class); + return false; + } + } + + /** + * Registers a new {@link DynamicallyActivatableTraceExporterService} that is used to export {@link io.opentelemetry.sdk.trace.data.SpanData} for sampled {@link io.opentelemetry.api.trace.Span}s + * + * @param service + * + * @return + */ + public boolean registerTraceExporterService(DynamicallyActivatableTraceExporterService service) { + if (null == registeredTraceExportServices) { + registeredTraceExportServices = new LinkedHashSet<>(); + } + try { + // try to add the service if it has not already been registered + if (registeredTraceExportServices.add(service)) { + notifyTracingSettingsChanged(); + log.info("The service {} was successfully registered.", service.getName()); + return true; + } else { + log.warn("The service {} was already registered", service.getName()); + return false; + } + // return configureOpenTelemetry(); + } catch (Exception e) { + log.error("Failed to register " + service.getName(), e); + return false; + } + + } + + public boolean unregisterTraceExporterService(DynamicallyActivatableTraceExporterService service) { + + if (registeredTraceExportServices.remove(service)) { + notifyTracingSettingsChanged(); + return true; + } else { + log.warn("Failed to unregister {}. The service has not been registered.", service.getName()); + return false; + } + } + + /** + * Register a {@link DynamicallyActivatableMetricsExporterService} + * + * @param service + * + * @return + */ + public boolean registerMetricExporterService(DynamicallyActivatableMetricsExporterService service) { + if (null == registeredMetricExporterServices) { + registeredMetricExporterServices = new LinkedHashSet<>(); + } + try { + if (registeredMetricExporterServices.add(service)) { + notifyMetricsSettingsChanged(); + log.info("The service {} was successfully registered.", service.getName()); + return true; + } else { + log.warn("The service {} was already registered!", service.getName()); + return false; + } + // return configureOpenTelemetry(); + } catch (Exception e) { + log.error("Failed to register " + service.getName(), e); + return false; + } + } + + public boolean unregisterMetricExporterService(DynamicallyActivatableMetricsExporterService service) { + if (registeredMetricExporterServices.remove(service)) { + notifyMetricsSettingsChanged(); + return true; + } else { + log.warn("Failed to unregister {}. The service has not been registered.", service.getName()); + return false; + } + } + + private synchronized boolean configureTracing(InspectitConfig configuration) { + if (!enabled) { + return true; + } + try { + // set up sampler + double probability = configuration.getTracing().getSampleProbability(); + sampler = Sampler.traceIdRatioBased(probability); + + // build TracerProvider + SdkTracerProviderBuilder builder = SdkTracerProvider.builder() + .setSampler(sampler) + .setResource(serviceNameResource); + + // add all SpanProcessors + for (DynamicallyActivatableTraceExporterService traceExportServices : registeredTraceExportServices) { + // TODO: or do we rather want to have a getter method in the service? + builder.addSpanProcessor(traceExportServices.getSpanProcessor()); + log.info("addSpanProcessor for service {}", traceExportServices); + } + + // add SpanProcessors that have been added by testing methods + for (SpanProcessor spanProcessor : spanProcessorsForTesting) { + builder.addSpanProcessor(spanProcessor); + } + + // build the SdkTracerProvider + SdkTracerProvider tracerProvider = builder.build(); + + // rebuild OTel + OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) + // TODO: do I also need the W3CBaggagePropagator or B3Propagator? + // W3CBaggagePropagator.getInstance() + .build(); + + // if the OpenTelemetryImpl has not been build, then build and register it + if (null == openTelemetry) { + openTelemetry = OpenTelemetryImpl.builder().openTelemetry(openTelemetrySdk).build(); + + // check if any OpenTelemetry has been registered to GlobalOpenTelemetry. + // If so, reset it. + if (null != OpenTelemetryUtils.getGlobalOpenTelemetry()) { + log.info("reset {}", GlobalOpenTelemetry.get().getClass().getName()); + GlobalOpenTelemetry.resetForTest(); + } + + // set GlobalOpenTelemetry + openTelemetry.registerGlobal(); + } + // otherwise, just update the underlying OpenTelemetrySdk + else { + openTelemetry.set(openTelemetrySdk); + } + + // update the OTEL_TRACER field in OpenTelemetrySpanBuilderImpl + OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); + + log.info("SpanExporter={}", Tracing.getExportComponent().getSpanExporter()); + return true; + } catch (Exception e) { + log.error("Failed to configure OpenTelemetry Tracing", e); + return false; + } + } + + /** + * Configures the {@link SdkMeterProvider} and registers it as the {@link GlobalMeterProvider} via {@link GlobalMeterProvider#set(MeterProvider)} + * + * @param configuration + * + * @return + */ + private synchronized boolean configureMeterProvider(InspectitConfig configuration) { + if (!enabled) { + return true; + } + try { + SdkMeterProviderBuilder builder = SdkMeterProvider.builder().setResource(serviceNameResource); + + for (DynamicallyActivatableMetricsExporterService metricsExportService : registeredMetricExporterServices) { + log.info("add metricReader for {} ({})", metricsExportService, metricsExportService.getMetricReaderFactory()); + builder.registerMetricReader(OpenCensusMetrics.attachTo(metricsExportService.getMetricReaderFactory())); + } + + SdkMeterProvider sdkMeterProvider = builder.build(); + + // if the MeterProvider is null, build and register it + if (null == meterProvider) { + meterProvider = MeterProviderImpl.builder().meterProvider(sdkMeterProvider).build(); + meterProvider.registerGlobal(); + } + // otherwise, just update the internally used SdkMeterProvider + else { + meterProvider.set(sdkMeterProvider); + } + + return true; + + } catch (Exception e) { + log.error("Failed to configure MeterProvider", e); + return false; + } + } + + /** + * See {@link rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableSpanExporter#registerHandler(String, DynamicallyActivatableSpanExporter.Handler)} + * + * @param name the name of the service + * @param name the name of the service handler. Must be unique for each service. + * @param serviceHandler the service handler that is called for each ended sampled {@link io.opentelemetry.api.trace.Span} + */ + public void registerHandler(String name, DynamicallyActivatableSpanExporter.Handler serviceHandler) { + // TODO: implement + } + + public void unregisterHandler(String name, DynamicallyActivatableSpanExporter.Handler serviceHandler) { + + // TODO: implement + + } + + /** + * List of {@link SpanProcessor}. + */ + Set spanProcessorsForTesting = new LinkedHashSet<>(); + + /** + * Registers a {@link SpanProcessor}. ONLY USE THIS METHOD FOR TESTING! + * + * @param spanProcessor + * + * @return Whether {@link #configureTracing(InspectitConfig)} was successful, i.e., {@link GlobalOpenTelemetry} was updated + */ + public boolean registerSpanProcessorForTesting(SpanProcessor spanProcessor) { + spanProcessorsForTesting.add(spanProcessor); + return configureTracing(env.getCurrentConfig()); + } + + /** + * Unregisters a {@link SpanProcessor}. ONLY USE THIS METHOD FOR TESTING + * + * @param spanProcessor + * + * @return Whether {@link #configureTracing(InspectitConfig)} was successful, i.e., {@link GlobalOpenTelemetry} was updated + */ + + public boolean unregisterSpanProcessorForTesting(SpanProcessor spanProcessor) { + spanProcessorsForTesting.remove(spanProcessor); + return configureTracing(env.getCurrentConfig()); + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java new file mode 100644 index 0000000000..adad3d92b6 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java @@ -0,0 +1,87 @@ +package rocks.inspectit.ocelot.core.opentelemetry; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.TracerProvider; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.common.CompletableResultCode; +import lombok.Builder; +import lombok.extern.slf4j.Slf4j; +import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; + +/** + * Custom implementation of {@link OpenTelemetry} that wraps {@link OpenTelemetrySdk}. + * This wrapper class is used to change {@link #openTelemetry} without the exception handling of {@link io.opentelemetry.api.GlobalOpenTelemetry#set(OpenTelemetry)} + */ +@Slf4j +@Builder +public class OpenTelemetryImpl implements OpenTelemetry { + + /** + * The {@link OpenTelemetrySdk} implementation + */ + private OpenTelemetrySdk openTelemetry; + + @Builder.Default + private Object lock = new Object(); + + /** + * Gets the currently registered {@link OpenTelemetry} or {@link OpenTelemetry#noop()} if nothing has been registered. + * + * @return + */ + public OpenTelemetry get() { + return null == openTelemetry ? OpenTelemetry.noop() : openTelemetry; + } + + @Override + public TracerProvider getTracerProvider() { + return get().getTracerProvider(); + } + + @Override + public ContextPropagators getPropagators() { + return get().getPropagators(); + } + + /** + * Registers the {@link OpenTelemetrySdk}. If an {@link OpenTelemetrySdk} was already registered as {@link #openTelemetry}, flush and close it before registering the new {@link OpenTelemetrySdk}. + * + * @param openTelemetry + */ + public void set(OpenTelemetrySdk openTelemetry) { + synchronized (lock) { + if (null != openTelemetry) { + log.info("Set new OpenTelemetry. Shut down previous ({})", openTelemetry.getClass()); + // stop previous SdkTracerProvider + OpenTelemetryUtils.stopTracerProvider(this.openTelemetry.getSdkTracerProvider()); + } + this.openTelemetry = openTelemetry; + } + } + + /** + * Shuts down the currently registered {@link #openTelemetry#getSdkTracerProvider() SdkTracerProvider} + * + * @return The {@link CompletableResultCode} + */ + public synchronized CompletableResultCode shutdown() { + CompletableResultCode result; + synchronized (lock) { + result = OpenTelemetryUtils.stopTracerProvider(openTelemetry.getSdkTracerProvider(), true); + } + return result; + } + + /** + * Registers {@link OpenTelemetryImpl this} to {@link GlobalOpenTelemetry#set(OpenTelemetry)} + * + * @return + */ + public OpenTelemetryImpl registerGlobal() { + GlobalOpenTelemetry.set(this); + return this; + } + +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/service/DynamicallyActivatableService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/service/DynamicallyActivatableService.java index d46299ae33..faefa47745 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/service/DynamicallyActivatableService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/service/DynamicallyActivatableService.java @@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -11,6 +13,7 @@ import rocks.inspectit.ocelot.config.model.exporters.metrics.PrometheusExporterSettings; import rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; +import rocks.inspectit.ocelot.core.opentelemetry.OpenTelemetryControllerImpl; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -29,6 +32,9 @@ public abstract class DynamicallyActivatableService { @Autowired protected InspectitEnvironment env; + @Autowired + protected OpenTelemetryControllerImpl openTelemetryController; + private List configDependencies; /** @@ -100,6 +106,7 @@ synchronized boolean disable() { } @EventListener(InspectitConfigChangedEvent.class) + @Order(Ordered.HIGHEST_PRECEDENCE) // make sure this is called before OpenTelemetryController#configureOpenTelemetry synchronized void checkForUpdates(InspectitConfigChangedEvent ev) { boolean affected = false; for (Expression exp : configDependencies) { @@ -120,6 +127,7 @@ synchronized void checkForUpdates(InspectitConfigChangedEvent ev) { if (checkEnabledForConfig(ev.getNewConfig())) { enable(); } + } } @@ -152,4 +160,11 @@ synchronized void checkForUpdates(InspectitConfigChangedEvent ev) { */ protected abstract boolean doDisable(); + /** + * Gets the name of the {@link DynamicallyActivatableService} + * @return The name of the {@link DynamicallyActivatableService} + */ + public String getName(){ + return getClass().getName(); + } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java new file mode 100644 index 0000000000..a0f6cd8738 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java @@ -0,0 +1,120 @@ +package rocks.inspectit.ocelot.core.utils; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Utility class to work with OpenTelemetry, i.e., {@link GlobalOpenTelemetry}, {@link SdkTracerProvider}, {@link SdkMeterProvider} + */ +@Slf4j +public class OpenTelemetryUtils { + + /** + * {@link SdkMeterProvider#close() closes} the given {@link SdkMeterProvider} and blocks waiting for it to complete. + * + * @param meterProvider The {@link SdkMeterProvider} to stop. + * + * @return The {@link CompletableResultCode} + */ + public static CompletableResultCode stopMeterProvider(SdkMeterProvider meterProvider) { + return stopMeterProvider(meterProvider, false); + } + + /** + * {@link SdkMeterProvider#close() Closes} the given {@link SdkMeterProvider} and optionally {@link SdkMeterProvider#forceFlush() force flushes}, and blocks waiting for it to complete. * + * @param meterProvider + * @param forceFlush Whether to call {@link SdkMeterProvider#forceFlush()} + * + * @return The {@link CompletableResultCode} + */ + public static CompletableResultCode stopMeterProvider(SdkMeterProvider meterProvider, boolean forceFlush) { + // force flush if applicable + long start = System.nanoTime(); + if (forceFlush) { + // wait until force flush has succeeded + CompletableResultCode flushResult = meterProvider.forceFlush(); + if (!flushResult.isDone()) { + CountDownLatch latch = new CountDownLatch(1); + long startFlush = System.nanoTime(); + flushResult.whenComplete(() -> latch.countDown()); + try { + latch.await(10, TimeUnit.SECONDS); + log.info("time to force flush SdkMeterProvider: {} ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startFlush)); + } catch (Throwable t) { + log.error("failed to force flush SdkMeterProvider", t); + t.printStackTrace(); + return CompletableResultCode.ofFailure(); + } + } + } + + // close the SdkMeterProvider. This calls shutDown internally. + meterProvider.close(); + log.info("time to stop {}: {} ms", meterProvider, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)); + return CompletableResultCode.ofSuccess(); + } + + /** + * Stops the given {@link SdkTracerProvider} and blocks waiting for it to complete. + * + * @param tracerProvider The {@link SdkTracerProvider} to shut down + */ + public static CompletableResultCode stopTracerProvider(SdkTracerProvider tracerProvider) { + return stopTracerProvider(tracerProvider, false); + } + + /** + * {@link SdkTracerProvider#close() stops} the given {@link SdkTracerProvider} and optionally {@link SdkTracerProvider#forceFlush() flushes} it before {@link SdkTracerProvider#close() closing}, and blocks waiting for it to complete. + * + * @param tracerProvider The {@link SdkTracerProvider} to stop + * @param forceFlush Whether to call {@link SdkTracerProvider#forceFlush()} before closing it. + */ + public static CompletableResultCode stopTracerProvider(SdkTracerProvider tracerProvider, boolean forceFlush) { + if (null != tracerProvider) { + long start = System.nanoTime(); + if (forceFlush) { + CompletableResultCode flushResultCode = tracerProvider.forceFlush(); + if (!flushResultCode.isDone()) { + long startFlush = System.nanoTime(); + CountDownLatch latch = new CountDownLatch(1); + flushResultCode.whenComplete(() -> latch.countDown()); + try { + latch.await(15, TimeUnit.SECONDS); + } catch (InterruptedException e) { + log.error("failed to forceFlush tracerProvider", e); + e.printStackTrace(); + return CompletableResultCode.ofFailure(); + } + log.info("time to force flush {}: {} ms", tracerProvider, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startFlush)); + } + } + tracerProvider.close(); + log.info("time to stop {}: {} ms", tracerProvider, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)); + } + return CompletableResultCode.ofSuccess(); + } + + /** + * Gets the {@link OpenTelemetry} registered at {@link GlobalOpenTelemetry#globalOpenTelemetry} without calling {@link GlobalOpenTelemetry#get()} to avoid that it is assigned to {@link OpenTelemetry#noop()} on the first call. + * + * @return The {@link OpenTelemetry} registered at {@link GlobalOpenTelemetry#globalOpenTelemetry} + */ + public static OpenTelemetry getGlobalOpenTelemetry() { + OpenTelemetry openTelemetry = null; + try { + Field field = ReflectionUtils.getPrivateField(GlobalOpenTelemetry.class, "globalOpenTelemetry"); + openTelemetry = (OpenTelemetry) field.get(null); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + return openTelemetry; + } +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java index 0204f150f9..a95cfd480b 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java @@ -20,7 +20,8 @@ import static org.awaitility.Awaitility.await; @TestPropertySource(properties = { - "inspectit.exporters.tracing.jaeger.url=http://127.0.0.1:14268/api/traces" + "inspectit.exporters.tracing.jaeger.url=http://127.0.0.1:14268/api/traces", + "inspectit.exporters.tracing.jaeger.grpc=http://127.0.0.1:14267/api/traces" }) @DirtiesContext public class JaegerExporterServiceIntTest extends SpringTestBase { diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java index 2e062a8a68..430020ec6a 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java @@ -16,6 +16,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.core.SpringTestBase; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; @@ -54,6 +55,7 @@ private void localSwitch(boolean enabled) { @Nested class EnableDisable { + @DirtiesContext @Test void testMasterSwitch() { updateProperties(props -> { @@ -62,6 +64,7 @@ void testMasterSwitch() { assertThat(service.isEnabled()).isFalse(); } + @DirtiesContext @Test void testLocalSwitch() { localSwitch(false); @@ -72,6 +75,7 @@ void testLocalSwitch() { @Nested class OpenTelemetryLogging { + @DirtiesContext @Test void verifyOpenTelemetryMetricsWritten() { // change export interval @@ -109,6 +113,7 @@ class OpenCensusLogging { StatsRecorder statsRecorder = Stats.getStatsRecorder(); + @DirtiesContext @Test void verifyOpenCensusMetricsWritten() throws InterruptedException { // change export interval diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java index 7a8d811176..94ddf72b95 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java @@ -13,6 +13,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.core.SpringTestBase; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; @@ -21,6 +22,9 @@ import static org.assertj.core.api.Assertions.assertThat; +/** + * Test class for {@link LoggingTraceExporterService} + */ public class LoggingTraceExporterServiceIntTest extends SpringTestBase { public static final String INSTRUMENTATION_NAME = "rocks.inspectit.ocelot.instrumentation"; @@ -33,12 +37,10 @@ public class LoggingTraceExporterServiceIntTest extends SpringTestBase { @Autowired LoggingTraceExporterService service; - @Autowired - InspectitEnvironment environment; - @BeforeEach void enableService() { localSwitch(true); + masterSwitch(true); } private void localSwitch(boolean enabled) { @@ -47,19 +49,26 @@ private void localSwitch(boolean enabled) { }); } + private void masterSwitch(boolean enabled) { + updateProperties(props -> { + props.setProperty("inspectit.tracing.enabled", enabled); + }); + } + @Nested class EnableDisable { + @DirtiesContext @Test void testMasterSwitch() { - updateProperties(props -> { - props.setProperty("inspectit.tracing.enabled", "false"); - }); + masterSwitch(false); assertThat(service.isEnabled()).isFalse(); } + @DirtiesContext @Test void testLocalSwitch() { + assertThat(service.isEnabled()).isTrue(); localSwitch(false); assertThat(service.isEnabled()).isFalse(); } @@ -87,43 +96,48 @@ private void makeSpans() { } } + @DirtiesContext @Test void verifyOpenTelemetryTraceSent() throws InterruptedException { assertThat(service.isEnabled()).isTrue(); makeSpans(); - Awaitility.waitAtMost(5, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { + Awaitility.waitAtMost(10, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { // assert that two traces have been logged assertThat(spanLogs.getEvents()).hasSize(2); // and the last contains our 'childOne' assertThat(spanLogs.getEvents().get(0).getMessage()).contains("openTelemetryChildSpan"); }); + // get number of logged events + int numEvents = spanLogs.size(); + // turn off trace exporter localSwitch(false); - // wait until everything is flushed - Thread.sleep(500); - - // get number of logged events - int numEvents = spanLogs.size(); + // wait until the service is shut down + Awaitility.waitAtMost(5, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(service.isEnabled()).isFalse()); // make sure no more spans are recorded - Thread.sleep(5000); + Thread.sleep(5000); // TODO: is there a better way than to sleep? assertThat(spanLogs.size()).isEqualTo(numEvents); // turn the trace exporter on again localSwitch(true); - Thread.sleep(1000); + Awaitility.waitAtMost(1, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + makeSpans(); // wait until the new spans are exported to the log - Awaitility.waitAtMost(5, TimeUnit.SECONDS) + Awaitility.waitAtMost(10, TimeUnit.SECONDS) .pollInterval(2, TimeUnit.SECONDS) .untilAsserted(() -> assertThat(spanLogs.size()).isEqualTo(numEvents + 2)); } + @DirtiesContext @Test void testLoggingExporterDisabled() throws InterruptedException { assertThat(service.isEnabled()).isTrue(); @@ -147,29 +161,22 @@ class OpenCensusLogging { @Test void testTracerRestart() { Tracer tracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); + // turn of the service localSwitch(false); - Awaitility.waitAtMost(5, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(environment.getCurrentConfig() - .getExporters() - .getTracing() - .getLogging() - .isEnabled()).isFalse()); + Awaitility.waitAtMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isFalse()); + // turn the service on again localSwitch(true); - OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); - Awaitility.waitAtMost(5, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(environment.getCurrentConfig() - .getExporters() - .getTracing() - .getLogging() - .isEnabled()).isTrue()); - Tracer newTracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); + Awaitility.waitAtMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + + //OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); + // make sure the new tracer is different + Tracer newTracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); assertThat(tracer).isNotSameAs(newTracer); } + @DirtiesContext @Test void verifyOpenCensusTraceSent() throws InterruptedException { assertThat(service.isEnabled()).isTrue(); @@ -186,12 +193,12 @@ void verifyOpenCensusTraceSent() throws InterruptedException { // turn off tracing exporter localSwitch(false); // make sure no more spans are recorded - Awaitility.await().untilAsserted(() -> assertThat(service.isEnabled()).isFalse()); + Awaitility.waitAtMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isFalse()); assertThat(spanLogs.size()).isEqualTo(numEvents); // turn tracing exporter back on localSwitch(true); - Awaitility.await().untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + Awaitility.waitAtMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); // make spans makeSpans(); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java index 0d47e54c6d..42b1880f3c 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java @@ -7,8 +7,11 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; import rocks.inspectit.ocelot.core.SpringTestBase; import java.io.IOException; @@ -16,12 +19,18 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; +@TestPropertySource(properties = {"inspecit.exporters.metrics.prometheus.enabled=true"}) + +@DirtiesContext public class PrometheusExporterServiceIntTest extends SpringTestBase { private static final int HTTP_TIMEOUT = 1000; private static CloseableHttpClient testClient; + @Autowired + PrometheusExporterService service; + @BeforeAll static void initClient() { RequestConfig.Builder requestBuilder = RequestConfig.custom(); @@ -45,11 +54,32 @@ void assertGet200(String url) throws Exception { } void assertUnavailable(String url) throws Exception { - Throwable throwable = catchThrowable(() -> testClient.execute(new HttpGet(url)).getStatusLine().getStatusCode()); + Throwable throwable = catchThrowable(() -> testClient.execute(new HttpGet(url)) + .getStatusLine() + .getStatusCode()); assertThat(throwable).isInstanceOf(IOException.class); } + /** + * Sets the switch of 'inspectit.exporters.metrics.prometheus.enabled' to the given value + * @param enabled + */ + void switchPrometheusExporterService(boolean enabled) { + updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.prometheus.enabled", enabled + ""); + }); + } + + /** + * Starts the {@link PrometheusExporterService} if it is not already running + */ + @BeforeEach + void enableService() { + switchPrometheusExporterService(true); + } + + @DirtiesContext @Test void testDefaultSettings() throws Exception { assertGet200("http://localhost:8888/metrics"); diff --git a/inspectit-ocelot-core/src/test/resources/config/SpringTestBase.yml b/inspectit-ocelot-core/src/test/resources/config/SpringTestBase.yml new file mode 100644 index 0000000000..2dee82f4b8 --- /dev/null +++ b/inspectit-ocelot-core/src/test/resources/config/SpringTestBase.yml @@ -0,0 +1,6 @@ +inspectit: + exporters: + metrics: + # disable prometheus to speed up the shutdown process of OpenTelemetryControllerImpl + prometheus: + enabled: false \ No newline at end of file From 6c981975b7495047958f5c33f4b404efd8fe7b0d Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 11:09:40 +0100 Subject: [PATCH 35/86] Removed unused classes (#1246) --- .../DynamicallyActivatableSampler.java | 72 ------------------- .../DynamicallyActivatableSpanProcessor.java | 63 ---------------- 2 files changed, 135 deletions(-) delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java deleted file mode 100644 index 67a5ebb4ed..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSampler.java +++ /dev/null @@ -1,72 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.context.Context; -import io.opentelemetry.sdk.trace.data.LinkData; -import io.opentelemetry.sdk.trace.samplers.Sampler; -import io.opentelemetry.sdk.trace.samplers.SamplingResult; -import lombok.Data; - -import java.util.List; - -/** - * A custom {@link Sampler} wrapper that can be dynamically enabled or disabled. - */ -@Data -public class DynamicallyActivatableSampler implements Sampler { - - private boolean enabled = true; - - /** - * The real implementation of the {@link Sampler} - */ - private Sampler sampler; - - - private DynamicallyActivatableSampler(Sampler sampler) { - this.sampler = sampler; - } - - /** - * Creates a new {@link DynamicallyActivatableSampler} for the given {@link Sampler} implementation - * @param sampler The underlying {@link Sampler} implementation - */ - public static DynamicallyActivatableSampler create(Sampler sampler){ - return new DynamicallyActivatableSampler(sampler); - } - - public static DynamicallyActivatableSampler createAlwaysOn() { - return new DynamicallyActivatableSampler(Sampler.alwaysOn()); - } - - public static DynamicallyActivatableSampler createAlwaysOff() { - return new DynamicallyActivatableSampler(Sampler.alwaysOff()); - } - - public static DynamicallyActivatableSampler createParentBased(Sampler root) { - return new DynamicallyActivatableSampler(Sampler.parentBased(root)); - } - - public static DynamicallyActivatableSampler createRatio(double ratio) { - return new DynamicallyActivatableSampler(Sampler.traceIdRatioBased(ratio)); - } - - @Override - public SamplingResult shouldSample(Context parentContext, String traceId, String name, SpanKind spanKind, Attributes attributes, List parentLinks) { - - // when enabled, call the 'real' sampler's shouldSample method - if (isEnabled()) { - return sampler.shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); - } - // otherwise, call the method of the alwaysOff() sampler - else { - return Sampler.alwaysOff().shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); - } - } - - @Override - public String getDescription() { - return String.format("Custom {}", sampler.getDescription()); - } -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java deleted file mode 100644 index 9eaebb6ff4..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanProcessor.java +++ /dev/null @@ -1,63 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.opentelemetry.context.Context; -import io.opentelemetry.sdk.trace.ReadWriteSpan; -import io.opentelemetry.sdk.trace.ReadableSpan; -import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import lombok.Data; - -/** - * A custom {@link SpanProcessor} wrapper that can be dynamically enabled or disabled. - */ -@Data -public class DynamicallyActivatableSpanProcessor implements SpanProcessor { - - private boolean enabled; - - /** - * The real implementation of the {@link SpanProcessor} - */ - private SpanProcessor spanProcessor; - - private DynamicallyActivatableSpanProcessor(SpanProcessor spanProcessor) { - this.spanProcessor = spanProcessor; - } - - public static DynamicallyActivatableSpanProcessor create(SpanProcessor spanProcessor) { - return new DynamicallyActivatableSpanProcessor(spanProcessor); - } - - public static DynamicallyActivatableSpanProcessor createSimpleSpanProcessor(SpanExporter spanExporter) { - return new DynamicallyActivatableSpanProcessor(SimpleSpanProcessor.create(spanExporter)); - } - - @Override - public void onStart(Context parentContext, ReadWriteSpan span) { - // if enabled, call the real processor's onStart - if (isEnabled()) { - spanProcessor.onStart(parentContext, span); - } - // otherwise, do nothing - } - - @Override - public boolean isStartRequired() { - return spanProcessor.isStartRequired(); - } - - @Override - public void onEnd(ReadableSpan span) { - // if enabled, call the real processor's onEnd - if (isEnabled()) { - spanProcessor.onEnd(span); - } - // otherwise, do nothing - } - - @Override - public boolean isEndRequired() { - return spanProcessor.isEndRequired(); - } -} From d25a6a0e2c837f30a35d494c50cf3dc4add8de22 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 11:13:55 +0100 Subject: [PATCH 36/86] Removed OpenCensusAgentMetricsExporterService and OpenCensusAgentTraceExporterService that are no longer supported with OpenTelemetry (#1246) --- ...OpenCensusAgentMetricsExporterService.java | 55 ------------------ .../OpenCensusAgentTraceExporterService.java | 58 ------------------- ...tryAgentMetricsExporterServiceIntTest.java | 6 -- ...metryAgentTraceExporterServiceIntTest.java | 6 -- 4 files changed, 125 deletions(-) delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterService.java delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterService.java delete mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentMetricsExporterServiceIntTest.java delete mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentTraceExporterServiceIntTest.java diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterService.java deleted file mode 100644 index 02784fa924..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentMetricsExporterService.java +++ /dev/null @@ -1,55 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.opencensus.common.Duration; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; -import rocks.inspectit.ocelot.config.model.InspectitConfig; -import rocks.inspectit.ocelot.config.model.exporters.metrics.OpenCensusAgentMetricsExporterSettings; -import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; - -import javax.validation.Valid; - -@Component -@Slf4j -public class OpenCensusAgentMetricsExporterService extends DynamicallyActivatableService { - - public OpenCensusAgentMetricsExporterService() { - super("exporters.metrics.openCensusAgent", "metrics.enabled"); - } - - @Override - protected boolean checkEnabledForConfig(InspectitConfig conf) { - @Valid OpenCensusAgentMetricsExporterSettings openCensusAgent = conf.getExporters().getMetrics().getOpenCensusAgent(); - return conf.getMetrics().isEnabled() - && openCensusAgent.isEnabled() - && !StringUtils.isEmpty(openCensusAgent.getAddress()); - } - - @Override - protected boolean doEnable(InspectitConfig configuration) { - try { - OpenCensusAgentMetricsExporterSettings settings = configuration.getExporters().getMetrics().getOpenCensusAgent(); - log.info("Starting OpenCensus Agent Metrics exporter"); - // TODO: implement OTel equivalent - /* - OcAgentMetricsExporter.createAndRegister(OcAgentMetricsExporterConfiguration.builder() - .setExportInterval(Duration.fromMillis(settings.getExportInterval().toMillis())) - .setEndPoint(settings.getAddress()) - .setServiceName(settings.getServiceName()) - .setUseInsecure(settings.isUseInsecure()) - .setRetryInterval(Duration.fromMillis(settings.getReconnectionPeriod().toMillis())).build()); - */ - return true; - } catch (Throwable t) { - log.error("Error creating OpenCensus Agent Metrics exporter", t); - return false; - } - } - - @Override - protected boolean doDisable() { - log.info("The OpenCensus Agent Metrics exporter cannot be stopped during runtime. In order to disable the exporter, the application has to be restarted."); - return false; - } -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterService.java deleted file mode 100644 index 88c66d8082..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OpenCensusAgentTraceExporterService.java +++ /dev/null @@ -1,58 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.opencensus.common.Duration; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; -import rocks.inspectit.ocelot.config.model.InspectitConfig; -import rocks.inspectit.ocelot.config.model.exporters.trace.OpenCensusAgentTraceExporterSettings; -import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; - -@Component -@Slf4j -public class OpenCensusAgentTraceExporterService extends DynamicallyActivatableService { - - public OpenCensusAgentTraceExporterService() { - super("exporters.tracing.openCensusAgent", "tracing.enabled"); - } - - @Override - protected boolean checkEnabledForConfig(InspectitConfig conf) { - OpenCensusAgentTraceExporterSettings openCensusAgent = conf.getExporters().getTracing().getOpenCensusAgent(); - return conf.getTracing().isEnabled() - && openCensusAgent.isEnabled() - && !StringUtils.isEmpty(openCensusAgent.getAddress()); - } - - @Override - protected boolean doEnable(InspectitConfig configuration) { - try { - OpenCensusAgentTraceExporterSettings settings = configuration.getExporters().getTracing().getOpenCensusAgent(); - log.info("Starting OpenCensus Agent Trace exporter"); - // TODO: implement OTel equivalent - /* - OcAgentTraceExporter.createAndRegister(OcAgentTraceExporterConfiguration.builder() - .setEndPoint(settings.getAddress()) - .setServiceName(settings.getServiceName()) - .setUseInsecure(settings.isUseInsecure()) - .setRetryInterval(Duration.fromMillis(settings.getReconnectionPeriod().toMillis())).build()); - */ - return true; - } catch (Throwable t) { - log.error("Error creating OpenCensus Agent Trace exporter", t); - return false; - } - } - - @Override - protected boolean doDisable() { - log.info("Stopping OpenCensus Agent Trace exporter"); - try { - // TODO: implement OTel equivalent - // OcAgentTraceExporter.unregister(); - } catch (Throwable t) { - log.error("Error disabling OpenCensus Agent Trace exporter", t); - } - return true; - } -} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentMetricsExporterServiceIntTest.java deleted file mode 100644 index 0446690ab4..0000000000 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentMetricsExporterServiceIntTest.java +++ /dev/null @@ -1,6 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -// TODO: implement according to the previously used OpenCensusAgentMetricsExporterServiceIntTest -public class OpenTelemetryAgentMetricsExporterServiceIntTest { - -} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentTraceExporterServiceIntTest.java deleted file mode 100644 index 8286760676..0000000000 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OpenTelemetryAgentTraceExporterServiceIntTest.java +++ /dev/null @@ -1,6 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -// TODO: implement this test class according to the previously used OpenCensusAgentTraceExporterServiceIntTest -public class OpenTelemetryAgentTraceExporterServiceIntTest { - -} From 8bce96356ef9752c912d834cfca557a7d19897ac Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 12:08:11 +0100 Subject: [PATCH 37/86] Updated the documentation of metric-exporters.md and trace-exporters.md (#1246) --- .../ocelot/config/default/exporters.yml | 2 +- .../docs/metrics/metric-exporters.md | 42 +++++++++---------- .../docs/tracing/trace-exporters.md | 30 ++++++------- 3 files changed, 32 insertions(+), 42 deletions(-) diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml index 434439daec..979c7299a2 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml @@ -51,7 +51,7 @@ inspectit: # settings for the LoggingMetricExporter used in LoggingExporterService logging: - enabled: false + enabled: true serviceName: ${inspectit.service-name} # the export interval of the metrics export-interval: ${inspectit.metrics.frequency} diff --git a/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md b/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md index 0f83b49165..faf8abddaa 100644 --- a/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md +++ b/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md @@ -10,15 +10,28 @@ If an exporter supports run-time updates it means that it can be enabled/disable This way you can, for example, change the endpoint where exporter pushes the metrics without a need to restart the application. In order to use run-time updates, you must enable one of the [externalized configuration methods](configuration/external-configuration-sources) that support dynamic updates. -inspectIT Ocelot currently supports the following OpenCensus metrics exporters: +inspectIT Ocelot currently supports the following OpenTelemetry metrics exporters: |Exporter |Supports run-time updates| Push / Pull |Enabled by default |---|---|---|---| -|[Prometheus Exporter](#prometheus-exporter)|Yes|Pull|Yes -|[OpenCensus Agent Exporter](#opencensus-agent-metrics-exporter)|No|Push|Yes -|[InfluxDB Exporter](#influxdb-exporter)|Yes|Push|Yes +|[Logging Exporter](#logging-exporter)|Yes|Pull|Yes +|[~~Prometheus Exporter~~](#prometheus-exporter)|Yes|Pull|Yes +|[~~InfluxDB Exporter~~](#influxdb-exporter)|Yes|Push|Yes + +>**Important note**: Starting with version `1.15.0`, inspectIT Ocelot moved from OpenCensus to OpenTelemetry. As a result, the `OpenCensus Agent Exporter` is no longer supported. +Currently, Prometheus and InfluxDB are **not** functional and will be re-implemented in the next version. + +## Logging Exporter + +The Logging exporter exports the metrics to the console. By default, the exporter is enabled. The following properties are nested properties below the `inspectit.exporters.metrics.logging`: + +|Property |Default| Description +|---|---|---| +|`.enabled`|`true`|If true, the inspectIT Ocelot agent will try to start the Logging metrics exporter. +|`.export-interval`|refers to `inspectit.metrics.frequency`|The export interval of the metrics. ## Prometheus Exporter +>**Important**: the Prometheus exporter is currently not working Prometheus exporter exposes the metrics in Prometheus format and is the default metrics exporter set up by inspectIT Ocelot. When enabled, inspectIT starts a Prometheus HTTP server in parallel with your application. @@ -33,27 +46,10 @@ The following properties are nested properties below the `inspectit.exporters.me |`.port`|`8888`|The port the Prometheus HTTP server should use. -> Don't forget to check [the official OpenCensus Prometheus exporter documentation](https://opencensus.io/exporters/supported-exporters/java/prometheus/). - -## OpenCensus Agent Metrics Exporter - -Metrics can be additionally exported to the [OpenCensus Agent](https://opencensus.io/service/components/agent/). -When enabled, all metrics are sent via gRCP to the OpenCensus Agent. By default, the exporter is enabled, but the agent address is set to `null`. - -The following properties are nested properties below the `inspectit.exporters.metrics.open-census-agent` property: - -|Property |Default| Description -|---|---|---| -|`.enabled`|`true`|If true, the agent will try to start the OpenCensus Agent Metrics exporter. -|`.address`|`null`|Address of the open-census agent (e.g. localhost:1234). -|`.use-insecure`|`false`|If true, SSL is disabled. -|`.service-name`|refers to `inspectit.service-name`|The service-name which will be used to publish the metrics. -|`.reconnection-period`|`5`|The time at which the exporter tries to reconnect to the OpenCensus agent. -|`.export-interval`|refers to `inspectit.metrics.frequency`|The export interval of the metrics. - -> Don't forget to check [the official OpenCensus Agent exporter documentation](https://opencensus.io/exporters/supported-exporters/java/ocagent/). +> Don't forget to check [the official OpenTelemetry Prometheus exporter documentation](https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/prometheus). ## InfluxDB Exporter +>**Important**: the InfluxDB exporter is currently not working If enabled, metrics are pushed at a specified interval directly to a given InfluxDB v1.x instance. To enable the InfluxDB Exporters, it is only required to specify the `url`. diff --git a/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md b/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md index d77c6096a9..526aeff9ee 100644 --- a/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md +++ b/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md @@ -7,11 +7,19 @@ Metrics exporters are responsible for passing the recorded tracing data to a cor inspectIT Ocelot currently supports the following OpenCensus trace exporters: -* [Zipkin](#zipkin-exporter) [[Homepage](https://zipkin.io/)] -* [Jaeger](#jaeger-exporter) [[Homepage](https://www.jaegertracing.io/)] -* [OpenCensus Agent](#opencensus-agent-trace-exporter) [[Homepage](https://opencensus.io/exporters/supported-exporters/java/ocagent/)] +* [Logging](#logging-exporter) [[Homepage](https://github.com/open-telemetry/opentelemetry-java/blob/main/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/LoggingSpanExporter.java)] +* [~~Zipkin~~](#zipkin-exporter) [[Homepage](https://zipkin.io/)] +* [~~Jaeger~~](#jaeger-exporter) [[Homepage](https://www.jaegertracing.io/)] + +>**Important note**: Starting with version `1.15.0`, inspectIT Ocelot moved from OpenCensus to OpenTelemetry. As a result, the `OpenCensus Agent Exporter` is no longer supported. +Currently, Zipkin and Jaeger are **not** functional and will be re-implemented in the next version. + +## Logging Exporter + +The Logging exporter exports traces to the system log. By default, the Logging exporter is enabled. ## Zipkin Exporter +>**Important**: the Zipkin exporter is currently not working The Zipkin exporter exports Traces in Zipkin v2 format to a Zipkin server or other compatible servers. It can be enabled and disabled via the `inspectit.exporters.tracing.zipkin.enabled` property. By default, the Zipkin exporter is enabled. It however does not have an URL configured. The exporter will start up as soon as you define the `inspectit.exporters.tracing.zipkin.url` property. @@ -26,6 +34,7 @@ When sending spans, Zipkin expects you to give a name of the service where the s ## Jaeger Exporter +>**Important**: the Jaeger exporter is currently not working The Jaeger exports works exactly the same way as the [Zipkin Exporter](#zipkin-exporter). The corresponding properties are the following: @@ -42,18 +51,3 @@ To make inspectIT Ocelot push the spans to a Jaeger server running on the same m ``` -Dinspectit.exporters.tracing.jaeger.url=http://127.0.0.1:14268/api/traces ``` - -## OpenCensus Agent Trace Exporter - -Spans can be additionally exported to the [OpenCensus Agent](https://opencensus.io/service/components/agent/). -When enabled, all Spans are sent via gRCP to the OpenCensus Agent. By default, the exporter is enabled, but the agent address is set to `null`. - -|Property |Default| Description -|---|---|---| -|`inspectit.exporters.tracing.open-census-agent.enabled`|`true`|If true, the agent will try to start the OpenCensus Agent Trace exporter. -|`inspectit.exporters.tracing.open-census-agent.address`|`null`|Address of the open-census agent (e.g. localhost:1234). -|`inspectit.exporters.tracing.open-census-agent.use-insecure`|`false`|If true, SSL is disabled. -|`inspectit.exporters.tracing.open-census-agent.service-name`|refers to `inspectit.service-name`|The service-name which will be used to publish the spans. -|`inspectit.exporters.tracing.open-census-agent.reconnection-period`|`5`|The time at which the exporter tries to reconnect to the OpenCensus agent. - -> Don't forget to check [the official OpenCensus Agent exporter documentation](https://opencensus.io/exporters/supported-exporters/java/ocagent/). \ No newline at end of file From 1496b79a480481d62ab0cdbf62e6a281ef4f2be8 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 12:53:27 +0100 Subject: [PATCH 38/86] Updated breaking-changes.md (#1246) --- .../docs/breaking-changes/breaking-changes.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md index 77fcf8ce1c..c5b584c9b9 100644 --- a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md +++ b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md @@ -2,6 +2,18 @@ id: Breaking Changes title: Breaking Changes --- +## Breaking changes in 1.15.0 + +### Integration of the OpenTelemetry OpenCensus Shim + +Starting with the current release, inspectIT Ocelot migrates from OpenCensus to [OpenTelemetry](https://github.com/open-telemetry). As a first step, we include the [OpenTelemetry OpenCensus Shim](https://github.com/open-telemetry/opentelemetry-java/tree/main/opencensus-shim). inspectIT Ocelot still uses and supports the [OpenCensus-API](https://opencensus.io/quickstart/java/), but the exporter implementations of OpenTelemetry are used. + +### Updated trace and metric exporter services + +Due to the migration from OpenCensus to OpenTelemetry, several trace and metric exporter services are disabled or have been removed. The only exporters supported in the current version are `Logging` [metric](metrics/metric-exporters.md#logging) and [trace](tracing/tracing-exporters.md#logging) exporters. + +The [Jaeger](tracing/tracing-exporters.md#jaeger-exporter) and [Zipkin](tracing/tracing-exporters.md#zipking-exporter) trace exporters as well as the [Prometheus](metrics/metric-exporters.md#prometheus-exporter) and [InfluxDB](metrics/metric-exporters.md#influxdb-exporter) metric exporters are disabled until they are properly integrated with OpenTelemetry. +The `OpenCensus Agent Exporter` (for metrics and traces) has been completely removed and will not be supported in the future. ## Breaking changes in 1.12.2 From 6de9e442be922124f94d65d78e969da20bdf1a0a Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 15:40:36 +0100 Subject: [PATCH 39/86] Added documentation for OpenTelemetry configuration in open-telemetry-configuration.md (#1246) --- .../open-telemetry-configuration.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 inspectit-ocelot-documentation/docs/configuration/open-telemetry-configuration.md diff --git a/inspectit-ocelot-documentation/docs/configuration/open-telemetry-configuration.md b/inspectit-ocelot-documentation/docs/configuration/open-telemetry-configuration.md new file mode 100644 index 0000000000..193c743558 --- /dev/null +++ b/inspectit-ocelot-documentation/docs/configuration/open-telemetry-configuration.md @@ -0,0 +1,34 @@ +--- +id: open-telemetry-configuration +title: Using OpenTelemetry Library with inspectIT Ocelot +sidebar_label: OpenTelemetry Configuration +--- + +If you plan to use the OpenTelemetry library in an application which will be instrumented later on with inspectIT Ocelot, some special rules do apply. +Following these rules will make sure that there are no run-time problems in your application. +Furthermore, a correct configuration will make it possible to combine metrics and traces that you manually collect using the OpenCensus instrumentation library with the ones collected by the inspectIT Ocelot agent. + +1. Make sure you are using the same version of OpenTelemetry as inspectIT Ocelot. + + The inspectIT Ocelot agent in version {inspectit-ocelot-version} internally uses OpenTelemetry in version {opentelemetry-version}. Please adapt any OpenCensus dependency in your application to this version to avoid run-time conflicts. + ```XML + + io.opentelemetry + opentelemetry-api + {opentelemetry-version} + + ``` + +2. Set the JVM property `inspectit.publishOpenTelemetryToBootstrap` to `true`. + + ``` + -Dinspectit.publishOpenTelemetryToBootstrap=true + ``` + + Setting the above property to `true` tells inspectIT Ocelot that you plan to use the OpenTelemetry library in combination with the agent. Note that this property must be specified with this exact name. The flexibility offered for all other config options does not apply here. The inspectIT Ocelot agent will make sure that all OpenTelemetry classes are then loaded by the bootstrap class loader. This ensures that OpenTelemetry implementation is shared between your manual instrumentation and the agent instrumentation, making the combination of data possible. + +3. Add the agent to the start of a JVM + + In this scenario, it is required that you add the agent via [the `javaagent` JVM argument](getting-started/installation.md#adding-the-agent-to-a-jvm). If the agent is successfully added to the JVM, it will log that the OpenTelemetry classes pushed to the bootstrap classloader will be used. + + It is important to state that the agent will *not* publish the OpenTelemetry classes to the bootstrap classloader if it is attached during runtime – even if the previously mentioned JVM argument is set! In this case, metrics and traces of *manual OpenTelemetry instrumentations* will *not* be collected by the inspectIT Ocelot agent. \ No newline at end of file From d51c3f3eb6f91593120ba868d87d5a9c50b92aaf Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 15:58:46 +0100 Subject: [PATCH 40/86] Refactored LoggingMetricExporterService and LoggingTraceExporterService by removing unnecessary or overcomplicated code/classes (#1246) --- .../DynamicallyActivatableExporter.java | 13 --- .../DynamicallyActivatableMetricExporter.java | 79 ----------------- .../DynamicallyActivatableSpanExporter.java | 85 ------------------- .../LoggingMetricExporterService.java | 9 +- .../exporter/LoggingTraceExporterService.java | 15 +--- 5 files changed, 7 insertions(+), 194 deletions(-) delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableExporter.java delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricExporter.java delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableExporter.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableExporter.java deleted file mode 100644 index 27a250aa8d..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableExporter.java +++ /dev/null @@ -1,13 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import rocks.inspectit.ocelot.config.model.InspectitConfig; - -public interface DynamicallyActivatableExporter { - - boolean doDisable(); - - boolean doEnable(); - - boolean configurationChanged(InspectitConfig config); - -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricExporter.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricExporter.java deleted file mode 100644 index d27932d469..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricExporter.java +++ /dev/null @@ -1,79 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.opentelemetry.exporter.logging.LoggingMetricExporter; -import io.opentelemetry.exporter.logging.LoggingSpanExporter; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.metrics.data.MetricData; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import lombok.*; -import lombok.extern.slf4j.Slf4j; - -import java.util.Collection; - -/** - * A custom {@link MetricExporter} wrapper that can be dynamically enabled or disabled. - */ -@Slf4j -@Data -public class DynamicallyActivatableMetricExporter implements MetricExporter { - - /** - * The real {@link MetricExporter} implementation - */ - private T exporter; - - @Setter(AccessLevel.PRIVATE) - private boolean enabled = false; - - public DynamicallyActivatableMetricExporter(T metricExporter) { - exporter = metricExporter; - } - - /** - * Creates a new {@link DynamicallyActivatableMetricExporter} with the given {@link MetricExporter} as the implemented exporter - * - * @param metricExporter - * - * @return {@link DynamicallyActivatableMetricExporter} with the given {@link MetricExporter} as the implemented exporter - */ - public static DynamicallyActivatableMetricExporter create(MetricExporter metricExporter) { - return new DynamicallyActivatableMetricExporter<>(metricExporter); - } - - /** - * Creates a new {@link DynamicallyActivatableMetricExporter} with a {@link LoggingMetricExporter} as the implemented {@link MetricExporter} - * - * @return {@link DynamicallyActivatableMetricExporter} with a {@link LoggingMetricExporter} as the implemented {@link MetricExporter} - */ - public static DynamicallyActivatableMetricExporter createLoggingExporter() { - return new DynamicallyActivatableMetricExporter<>(new LoggingMetricExporter()); - } - - @Override - public CompletableResultCode export(Collection metrics) { - // if enabled, call the real exporter's export method, otherwise do nothing - return isEnabled() ? exporter.export(metrics) : CompletableResultCode.ofSuccess(); - } - - @Override - public CompletableResultCode flush() { - return exporter.flush(); - } - - @Override - public CompletableResultCode shutdown() { - return exporter.shutdown(); - } - - public boolean doEnable() { - setEnabled(true); - return true; - } - - public boolean doDisable() { - setEnabled(false); - return true; - } -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java deleted file mode 100644 index dee4ca2c49..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableSpanExporter.java +++ /dev/null @@ -1,85 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.opentelemetry.exporter.logging.LoggingSpanExporter; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import lombok.AccessLevel; -import lombok.Data; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -import java.util.Collection; - -/** - * A custom {@link SpanExporter} wrapper that can be dynamically enabled or disabled. - */ -@Data -@Slf4j -public class DynamicallyActivatableSpanExporter implements SpanExporter { - - @Setter(AccessLevel.PRIVATE) - private boolean enabled; - - /** - * The underlying implementation of the {@link SpanExporter} - */ - private T exporter; - - private DynamicallyActivatableSpanExporter(T spanExporter) { - exporter = spanExporter; - } - - /** - * Creates a new {@link DynamicallyActivatableSpanExporter} with the given {@link SpanExporter} as the implemented exporter - * - * @param spanExporter - * - * @return {@link DynamicallyActivatableSpanExporter} with the given {@link SpanExporter} as the implemented exporter - */ - public static DynamicallyActivatableSpanExporter create(SpanExporter spanExporter) { - return new DynamicallyActivatableSpanExporter(spanExporter); - } - - /** - * Creates a new {@link DynamicallyActivatableSpanExporter} with a {@link LoggingSpanExporter} as the implemented {@link SpanExporter} - * - * @return {@link DynamicallyActivatableSpanExporter} with a {@link LoggingSpanExporter} as the implemented {@link SpanExporter} - */ - public static DynamicallyActivatableSpanExporter createLoggingSpanExporter() { - return new DynamicallyActivatableSpanExporter<>(new LoggingSpanExporter()); - } - - @Override - public CompletableResultCode export(Collection spans) { - // if enabled, call the real exporter's export method - if (isEnabled()) { - return exporter.export(spans); - } - // otherwise, do nothing and return success - else { - return CompletableResultCode.ofSuccess(); - } - } - - @Override - public CompletableResultCode flush() { - return exporter.flush(); - } - - @Override - public CompletableResultCode shutdown() { - return exporter.shutdown(); - } - - public boolean doEnable() { - setEnabled(true); - return true; - } - - public boolean doDisable() { - setEnabled(false); - return true; - } - -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java index 67b2868ede..ee5813edcd 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java @@ -29,9 +29,9 @@ public class LoggingMetricExporterService extends DynamicallyActivatableService private SdkMeterProvider meterProvider; /** - * The {@link DynamicallyActivatableMetricExporter} for exporting metrics to the log + * The {@link LoggingMetricExporter} for exporting metrics to the system log */ - private DynamicallyActivatableMetricExporter metricExporter; + private LoggingMetricExporter metricExporter; /** * The {@link PeriodicMetricReader} for reading metrics to the log @@ -47,7 +47,7 @@ protected void init() { super.init(); // create new metric exporter - metricExporter = DynamicallyActivatableMetricExporter.createLoggingExporter(); + metricExporter = new LoggingMetricExporter(); // close the tracer provider when the JVM is shutting down Runtime.getRuntime().addShutdownHook(new Thread(() -> { @@ -75,8 +75,6 @@ protected boolean doEnable(InspectitConfig configuration) { .registerMetricReader(OpenCensusMetrics.attachTo(metricReader.newMetricReaderFactory())) .buildAndRegisterGlobal(); - // enable the metric exporter - metricExporter.doEnable(); log.info("Starting LoggingMetricsExporter"); return true; } catch (Exception e) { @@ -95,7 +93,6 @@ protected boolean doDisable() { meterProvider.close(); meterProvider = null; } - metricExporter.doDisable(); log.info("Stopping LoggingMetricExporter"); return true; } catch (Exception e) { diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java index 904eecca55..895de208bd 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java @@ -49,14 +49,14 @@ public class LoggingTraceExporterService extends DynamicallyActivatableService { private Sampler sampler; /** - * The {@link SpanProcessor} of the {@link #spanExporter + * The {@link SpanProcessor} of the {@link #spanExporter} */ private SpanProcessor simpleSpanProcessor; /** - * The {@link DynamicallyActivatableSpanExporter< LoggingSpanExporter >} for exporting the spans to the log + * The {@link LoggingSpanExporter} for exporting the spans to the log */ - private DynamicallyActivatableSpanExporter spanExporter; + private LoggingSpanExporter spanExporter; /** * The service name {@link Resource} @@ -72,7 +72,7 @@ protected void init() { super.init(); // create span exporter and span processors - spanExporter = DynamicallyActivatableSpanExporter.createLoggingSpanExporter(); + spanExporter = new LoggingSpanExporter(); // close the tracer provider when the JVM is shutting down Runtime.getRuntime().addShutdownHook(new Thread(() -> { @@ -123,9 +123,6 @@ protected boolean doEnable(InspectitConfig conf) { // update OC tracer OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); - // enable span exporter - spanExporter.doEnable(); - log.info("Starting TraceLoggingSpanExporter"); return true; } catch (Exception e) { @@ -137,10 +134,6 @@ protected boolean doEnable(InspectitConfig conf) { @Override protected boolean doDisable() { try { - // disable the span exporter - if (null != spanExporter) { - spanExporter.doDisable(); - } // close the tracerProvider if (null != tracerProvider) { tracerProvider.forceFlush(); From 34f63062589487395f08f8bc6b5599f566c8b831 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 11 Jan 2022 16:19:04 +0100 Subject: [PATCH 41/86] Fixed failing tests in ModelAutoCompleterTest (#1246) --- .../autocompleterimpl/ModelAutoCompleterTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleterTest.java b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleterTest.java index e81c2f2ea2..5e8d9e0abf 100644 --- a/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleterTest.java +++ b/components/inspectit-ocelot-configurationserver/src/test/java/rocks/inspectit/ocelot/autocomplete/autocompleterimpl/ModelAutoCompleterTest.java @@ -52,7 +52,7 @@ void pastList() { List result = completer.getSuggestions(input); - assertThat(result).containsExactlyInAnyOrder("config", "env", "exporters", "instrumentation", "logging", "metrics", "plugins", "privacy", "publish-open-census-to-bootstrap", "self-monitoring", "service-name", "tags", "thread-pool-size", "tracing", "agent-commands"); + assertThat(result).containsExactlyInAnyOrder("config", "env", "exporters", "instrumentation", "logging", "metrics", "plugins", "privacy", "publish-open-telemetry-to-bootstrap", "self-monitoring", "service-name", "tags", "thread-pool-size", "tracing", "agent-commands"); } @Test @@ -124,7 +124,7 @@ void startsNotLikeInspectit() { @Test void startsNotWithInspectit() { - List input = Arrays.asList("test", "antotherTest"); + List input = Arrays.asList("test", "anotherTest"); List result = completer.getSuggestions(input); @@ -139,7 +139,7 @@ public class GetProperties { void getPropertiesInspectit() { List result = completer.getProperties(InspectitConfig.class); - assertThat(result).containsExactlyInAnyOrder("config", "env", "exporters", "instrumentation", "logging", "metrics", "plugins", "privacy", "publish-open-census-to-bootstrap", "self-monitoring", "service-name", "tags", "thread-pool-size", "tracing", "agent-commands"); + assertThat(result).containsExactlyInAnyOrder("config", "env", "exporters", "instrumentation", "logging", "metrics", "plugins", "privacy", "publish-open-telemetry-to-bootstrap", "self-monitoring", "service-name", "tags", "thread-pool-size", "tracing", "agent-commands"); } } } From 4b20a8b1c12e1faaec6c6b94de69e5751ed58740 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 13 Jan 2022 09:12:12 +0100 Subject: [PATCH 42/86] #1269: Changed OpenTelemetryControllerImpl to not rebuild the SdkTracerProvider when individual trace exporter services change. For this, the DynamicMultiSpanExporter and DynamicSampler have been implemented that can be updated while OpenTelemetry is running. The DynamicallyActivatableTraceExporterService now expose the spanExporter instead of the spanProcessor. --- .../inspectit/ocelot/utils/TestUtils.java | 9 + .../IOpenTelemetryController.java | 42 +- .../NoopOpenTelemetryController.java | 20 +- .../exporter/DynamicMultiSpanExporter.java | 109 +++++ ...icallyActivatableTraceExporterService.java | 10 +- .../core/exporter/JaegerExporterService.java | 17 +- .../exporter/LoggingTraceExporterService.java | 21 +- .../exporter/OtlpTraceExporterService.java | 21 +- .../core/exporter/ZipkinExporterService.java | 15 +- .../core/opentelemetry/DynamicSampler.java | 63 +++ .../core/opentelemetry/MeterProviderImpl.java | 32 +- .../OpenTelemetryControllerImpl.java | 461 ++++++++++-------- .../core/opentelemetry/OpenTelemetryImpl.java | 21 +- .../DynamicallyActivatableService.java | 7 +- .../ocelot/core/utils/OpenTelemetryUtils.java | 8 +- .../LoggingTraceExporterServiceIntTest.java | 41 +- 16 files changed, 570 insertions(+), 327 deletions(-) create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicMultiSpanExporter.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/DynamicSampler.java diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java index 21cca8871f..7da8cdd7a1 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java @@ -31,6 +31,8 @@ import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import org.awaitility.core.ConditionTimeoutException; import rocks.inspectit.ocelot.bootstrap.AgentManager; +import rocks.inspectit.ocelot.bootstrap.Instances; +import rocks.inspectit.ocelot.bootstrap.opentelemetry.NoopOpenTelemetryController; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -313,6 +315,13 @@ public static TimeSeries getTimeseries(String metricName, Map ta */ public static InMemorySpanExporter initializeOpenTelemetryForSystemTesting() { + if (NoopOpenTelemetryController.INSTANCE != Instances.openTelemetryController) { + System.out.println("shut down " + Instances.openTelemetryController.getClass().getSimpleName()); + + // shut down any previously configured OTELs + Instances.openTelemetryController.shutdown(); + Instances.openTelemetryController = NoopOpenTelemetryController.INSTANCE; + } // create an SdkTracerProvider with InMemorySpanExporter and LoggingSpanExporter InMemorySpanExporter inMemSpanExporter = InMemorySpanExporter.create(); diff --git a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java index 874cdb0446..11c601ba25 100644 --- a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java +++ b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java @@ -6,16 +6,13 @@ public interface IOpenTelemetryController { /** - * Shuts down the {@link IOpenTelemetryController} - */ - void shutdown(); - - /** - * Starts the {@link IOpenTelemetryController} + * Returns a completely noop {@link IOpenTelemetryController} * - * @return Whether the {@link IOpenTelemetryController} was successfuly started + * @return */ - boolean start(); + static IOpenTelemetryController noop() { + return NoopOpenTelemetryController.INSTANCE; + } /** * Gets whether the {@link IOpenTelemetryController} is configured @@ -32,12 +29,37 @@ public interface IOpenTelemetryController { boolean isEnabled(); /** - * Notifies the {@link IOpenTelemetryController} that something in the {@link rocks.inspectit.ocelot.config.model.tracing.TracingSettings} changed + * Gets whether the {@link IOpenTelemetryController} has been stopped. Once stopped, it cannot be restarted + * + * @return Whether the {@link IOpenTelemetryController} has been stopped. + */ + boolean isStopped(); + + /** + * Starts the {@link IOpenTelemetryController} + * + * @return Whether the {@link IOpenTelemetryController} was successfully started + */ + boolean start(); + + /** + * Flushes all pending spans and metrics and waits for it to complete. + */ + void flush(); + + /** + * Shuts down the {@link IOpenTelemetryController}. + * The shutdown is final, i.e., once this {@link IOpenTelemetryController} is shutdown, it cannot be re-enabled! + */ + void shutdown(); + + /** + * Notifies the {@link IOpenTelemetryController} that something in the {@link rocks.inspectit.ocelot.config.model.tracing.TracingSettings} or in the {@link rocks.inspectit.ocelot.config.model.exporters.trace.TraceExportersSettings} changed */ void notifyTracingSettingsChanged(); /** - * Notifies the {@link IOpenTelemetryController} that something in the {@link rocks.inspectit.ocelot.config.model.metrics.MetricsSettings} changed + * Notifies the {@link IOpenTelemetryController} that something in the {@link rocks.inspectit.ocelot.config.model.metrics.MetricsSettings} or in the {@link rocks.inspectit.ocelot.config.model.exporters.metrics.MetricsExportersSettings} changed */ void notifyMetricsSettingsChanged(); } diff --git a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java index 08fd723e5d..38343aff4d 100644 --- a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java +++ b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java @@ -5,8 +5,13 @@ public class NoopOpenTelemetryController implements IOpenTelemetryController { public static final NoopOpenTelemetryController INSTANCE = new NoopOpenTelemetryController(); @Override - public void shutdown() { + public boolean isConfigured() { + return false; + } + @Override + public boolean isEnabled() { + return true; } @Override @@ -15,13 +20,18 @@ public boolean start() { } @Override - public boolean isConfigured() { + public boolean isStopped() { return false; } @Override - public boolean isEnabled() { - return true; + public void shutdown() { + + } + + @Override + public void flush() { + } @Override @@ -31,6 +41,6 @@ public void notifyTracingSettingsChanged() { @Override public void notifyMetricsSettingsChanged() { - + } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicMultiSpanExporter.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicMultiSpanExporter.java new file mode 100644 index 0000000000..8469cbe78b --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicMultiSpanExporter.java @@ -0,0 +1,109 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +/** + * {@link SpanExporter} that forwards all received spans to a list of {@link SpanExporter}, similar to {@link io.opentelemetry.sdk.trace.export.MultiSpanExporter}. In contrast to {@link io.opentelemetry.sdk.trace.export.MultiSpanExporter}, {@link SpanExporter}s can by dynamically registered and unregisted. + * + *

Can be used to export to multiple backends using the same {@link io.opentelemetry.sdk.trace.SpanProcessor} like {@link io.opentelemetry.sdk.trace.export.SimpleSpanProcessor} or {@link io.opentelemetry.sdk.trace.export.BatchSpanProcessor} + */ +@Slf4j +public class DynamicMultiSpanExporter implements SpanExporter { + + /** + * The {@link SpanExporter}s that all received spans are forwarded to. + */ + private Map spanExporters = new ConcurrentHashMap<>(); + + private Object lock = new Object(); + + @Override + public CompletableResultCode export(Collection spans) { + CompletableResultCode resultCode; + synchronized (lock) { + resultCode = execute(spanExporters.values(), (spanExporter) -> spanExporter.export(spans)); + } + return resultCode; + } + + @Override + public CompletableResultCode flush() { + CompletableResultCode resultCode; + synchronized (lock) { + resultCode = execute(spanExporters.values(), (spanExporter) -> spanExporter.flush()); + } + return resultCode; + } + + @Override + public CompletableResultCode shutdown() { + CompletableResultCode resultCode; + synchronized (lock) { + resultCode = execute(spanExporters.values(), (spanExporter) -> spanExporter.shutdown()); + } + return resultCode; + } + + /** + * Executes the given function on a {@link Collection} + * + * @param spanExporters The {@link SpanExporter}s + * @param spanExporterFun The {@link Function} to apply on each {@link SpanExporter} + * + * @return The {@link CompletableResultCode} of all applications of the function + */ + private static CompletableResultCode execute(Collection spanExporters, Function spanExporterFun) { + List resultCodes = new ArrayList<>(spanExporters.size()); + for (SpanExporter spanExporter : spanExporters) { + CompletableResultCode resultCode; + try { + resultCode = spanExporterFun.apply(spanExporter); + } catch (RuntimeException e) { + log.error("Exception thrown in execute", e); + resultCodes.add(CompletableResultCode.ofFailure()); + continue; + } + resultCodes.add(resultCode); + } + return CompletableResultCode.ofAll(resultCodes); + } + + /** + * Registers the given {@link SpanExporter} to export {@link SpanData} for sampled spans. + * + * @param registerName The name of the span exporter service. Must be unique for each service. + * @param spanExporter The {@link SpanExporter} that is called for each {@link #export(Collection)}, {@link #flush()}, and {@link #shutdown()} + * + * @return Whether a {@link SpanProcessor} was **not** previously registered with the same name. Returns false if a {@link SpanExporter} with the given name was already registered. + */ + public boolean registerSpanExporter(String registerName, SpanExporter spanExporter) { + return null == spanExporters.put(registerName, spanExporter); + } + + /** + * Unregisters the given {@link SpanExporter} + * + * @param registerName The name of the span exporter service. + * + * @return Whether a {@link SpanExporter} with the given name was successfully removed. Returns false if no {@link SpanExporter} with the given name was previously registered. + */ + public boolean unregisterSpanExporter(String registerName) { + return null != spanExporters.remove(registerName); + } + + @Override + public String toString() { + return "SpanExporterImpl{" + "spanExporters=" + spanExporters + '}'; + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java index 1c001e2723..44eca549d0 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java @@ -1,7 +1,6 @@ package rocks.inspectit.ocelot.core.exporter; -import io.opentelemetry.sdk.trace.SpanProcessor; -import lombok.Getter; +import io.opentelemetry.sdk.trace.export.SpanExporter; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.config.model.exporters.metrics.PrometheusExporterSettings; import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; @@ -13,11 +12,11 @@ public abstract class DynamicallyActivatableTraceExporterService extends DynamicallyActivatableService { /** - * Gets the {@link SpanProcessor} used to process {@link io.opentelemetry.api.trace.Span} + * Gets the {@link SpanExporter} of this service to export {@link io.opentelemetry.sdk.trace.data.SpanData} to * - * @return The {@link SpanProcessor} used to process {@link io.opentelemetry.api.trace.Span} + * @return The {@link SpanExporter} of this service to export {@link io.opentelemetry.sdk.trace.data.SpanData} to */ - public abstract SpanProcessor getSpanProcessor(); + public abstract SpanExporter getSpanExporter(); /** * Constructor. @@ -31,5 +30,4 @@ public DynamicallyActivatableTraceExporterService(String... configDependencies) super(configDependencies); } - } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java index ec1da1dfe3..d92038f1dc 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java @@ -2,8 +2,6 @@ import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter; import io.opentelemetry.exporter.jaeger.thrift.JaegerThriftSpanExporter; -import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -23,10 +21,8 @@ public class JaegerExporterService extends DynamicallyActivatableTraceExporterSe private JaegerGrpcSpanExporter grpcSpanExporter; - private JaegerThriftSpanExporter spanExporter; - @Getter - private SpanProcessor spanProcessor; + private JaegerThriftSpanExporter spanExporter; public JaegerExporterService() { super("exporters.tracing.jaeger", "tracing.enabled"); @@ -55,7 +51,6 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { protected boolean doEnable(InspectitConfig configuration) { try { JaegerExporterSettings settings = configuration.getExporters().getTracing().getJaeger(); - log.info("Starting Jaeger Exporter with url '{}' (grpc '{}')", settings.getUrl(), settings.getGrpc()); // TODO: use getUrl() or getGRPC()? @@ -66,9 +61,6 @@ protected boolean doEnable(InspectitConfig configuration) { // create span exporter spanExporter = JaegerThriftSpanExporter.builder().setEndpoint(settings.getUrl()).build(); - // create span processor - spanProcessor = BatchSpanProcessor.builder(spanExporter).build(); - // register openTelemetryController.registerTraceExporterService(this); @@ -83,11 +75,10 @@ protected boolean doEnable(InspectitConfig configuration) { protected boolean doDisable() { log.info("Stopping Jaeger Exporter"); try { - if (null != spanProcessor) { - spanProcessor.shutdown(); - spanProcessor = null; - } openTelemetryController.unregisterTraceExporterService(this); + if (null != spanExporter) { + spanExporter.close(); + } } catch (Throwable t) { log.error("Error disabling Jaeger exporter", t); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java index bb049eff69..8510fdca54 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java @@ -1,15 +1,11 @@ package rocks.inspectit.ocelot.core.exporter; import io.opentelemetry.exporter.logging.LoggingSpanExporter; -import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.config.model.exporters.trace.LoggingTraceExporterSettings; -import rocks.inspectit.ocelot.core.opentelemetry.OpenTelemetryControllerImpl; import javax.validation.Valid; @@ -23,11 +19,8 @@ public class LoggingTraceExporterService extends DynamicallyActivatableTraceExpo /** * The {@link LoggingSpanExporter} for exporting the spans to the log */ - private LoggingSpanExporter spanExporter; - @Getter - private SpanProcessor spanProcessor; - + private LoggingSpanExporter spanExporter; public LoggingTraceExporterService() { super("exporters.tracing.logging", "tracing.enabled"); @@ -53,10 +46,6 @@ protected boolean doEnable(InspectitConfig conf) { LoggingTraceExporterSettings logging = conf.getExporters().getTracing().getLogging(); try { - // create span processors - // SpanProcessors are also shut down when the corresponding TracerProvider is shut down. Thus, we need to create the SpanProcessors each time - spanProcessor = SimpleSpanProcessor.create(spanExporter); - boolean success = openTelemetryController.registerTraceExporterService(this); if (success) { log.info("Starting {}", getClass().getSimpleName()); @@ -74,12 +63,10 @@ protected boolean doEnable(InspectitConfig conf) { @Override protected boolean doDisable() { try { - // shut down the span processor - if (null != spanProcessor) { - spanProcessor.shutdown(); - spanProcessor = null; - } openTelemetryController.unregisterTraceExporterService(this); + if (null != spanExporter) { + spanExporter.flush(); + } log.info("Stopping TraceLoggingSpanExporter"); return true; } catch (Exception e) { diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java index 27a12b6e49..864f463251 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java @@ -1,8 +1,6 @@ package rocks.inspectit.ocelot.core.exporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; -import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -21,11 +19,8 @@ public class OtlpTraceExporterService extends DynamicallyActivatableTraceExporterService { @Getter - private SpanProcessor spanProcessor; - private OtlpGrpcSpanExporter spanExporter; - public OtlpTraceExporterService() { super("exporters.tracing.otlp", "tracing.enabled"); } @@ -45,9 +40,6 @@ protected boolean doEnable(InspectitConfig configuration) { // create span exporter spanExporter = OtlpGrpcSpanExporter.builder().setEndpoint(otlp.getUrl()).build(); - // create span processor - spanProcessor = BatchSpanProcessor.builder(spanExporter).build(); - // register service openTelemetryController.registerTraceExporterService(this); return true; @@ -62,17 +54,12 @@ protected boolean doDisable() { log.info("Stopping OTLP trace exporter"); try { - - // shut down span processor - if (null != spanProcessor) { - spanProcessor.shutdown(); - spanProcessor = null; - } - // unregister service openTelemetryController.unregisterTraceExporterService(this); - } - catch (Throwable t){ + if (null != spanExporter) { + spanExporter.close(); + } + } catch (Throwable t) { log.error("Error disabling OTLP trace exporter", t); } return true; diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java index 6931ca7b15..9b148fc3ec 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java @@ -20,10 +20,8 @@ @Slf4j public class ZipkinExporterService extends DynamicallyActivatableTraceExporterService { - private ZipkinSpanExporter spanExporter; - @Getter - private SpanProcessor spanProcessor; + private ZipkinSpanExporter spanExporter; public ZipkinExporterService() { super("exporters.tracing.zipkin", "tracing.enabled"); @@ -44,9 +42,6 @@ protected boolean doEnable(InspectitConfig configuration) { // create span exporter spanExporter = ZipkinSpanExporter.builder().setEndpoint(settings.getUrl()).build(); - // create span processor - spanProcessor = BatchSpanProcessor.builder(spanExporter).build(); - // register openTelemetryController.registerTraceExporterService(this); return true; @@ -60,12 +55,10 @@ protected boolean doEnable(InspectitConfig configuration) { protected boolean doDisable() { log.info("Stopping Zipkin Exporter"); try { - // shut down the span processor - if (null != spanProcessor) { - spanProcessor.shutdown(); - spanProcessor = null; - } openTelemetryController.unregisterTraceExporterService(this); + if (null != spanExporter) { + spanExporter.close(); + } } catch (Throwable t) { log.error("Error disabling Zipkin exporter", t); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/DynamicSampler.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/DynamicSampler.java new file mode 100644 index 0000000000..d1d8bb8f01 --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/DynamicSampler.java @@ -0,0 +1,63 @@ +package rocks.inspectit.ocelot.core.opentelemetry; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; +import lombok.Getter; + +import java.util.List; + +/** + * Custom implementation of {@link Sampler} that wraps {@link io.opentelemetry.sdk.trace.samplers.TraceIdRatioBasedSampler}. + * This wrapper is used to change the {@link #sampleProbability} without resetting {@link io.opentelemetry.api.GlobalOpenTelemetry#set(OpenTelemetry)}. + * For use, create the {@link DynamicSampler} and set it once to {@link io.opentelemetry.sdk.trace.SdkTracerProviderBuilder#setSampler(Sampler)}. + */ +public class DynamicSampler implements Sampler { + + /** + * The {@link Sampler} implementation. + */ + private Sampler sampler; + + /** + * The sample probability. + */ + @Getter + private double sampleProbability; + + /** + * Creates a new {@link DynamicSampler} with the given sample probability + * @param sampleProbability The sample probability, see {@link rocks.inspectit.ocelot.config.model.tracing.TracingSettings#sampleProbability} and {@link Sampler#traceIdRatioBased(double)} + */ + public DynamicSampler(double sampleProbability) { + this.sampleProbability = sampleProbability; + this.sampler = Sampler.traceIdRatioBased(sampleProbability); + } + + @Override + public SamplingResult shouldSample(Context parentContext, String traceId, String name, SpanKind spanKind, Attributes attributes, List parentLinks) { + return sampler.shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); + } + + @Override + public String getDescription() { + return sampler.getDescription(); + } + + /** + * Sets the sample probability. If the given sample probability does not equal {@link #sampleProbability}, a new {@link io.opentelemetry.sdk.trace.samplers.TraceIdRatioBasedSampler} is created via {@link Sampler#traceIdRatioBased(double)} + * + * @param sampleProbability The sample probability + */ + public void setSampleProbability(double sampleProbability) { + if (this.sampleProbability != sampleProbability) { + this.sampleProbability = sampleProbability; + this.sampler = Sampler.traceIdRatioBased(sampleProbability); + } + } + +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java index 0596564bfa..14c1b42877 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java @@ -9,6 +9,9 @@ import lombok.extern.slf4j.Slf4j; import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + /** * Custom implementation of {@link MeterProvider} that wraps {@link SdkMeterProvider}. * This wrapper is used to change {@link #meterProvider} without resetting {@link io.opentelemetry.api.metrics.GlobalMeterProvider#set(MeterProvider)}. @@ -53,11 +56,11 @@ public SdkMeterProvider get() { } /** - * Shuts down the currently registered {@link #meterProvider} and blocks waiting for it. + * Shuts down the currently registered {@link #meterProvider} and blocks waiting for it to complete. * * @return */ - public CompletableResultCode shutdown() { + public CompletableResultCode close() { CompletableResultCode result; synchronized (lock) { result = OpenTelemetryUtils.stopMeterProvider(meterProvider, true); @@ -65,6 +68,31 @@ public CompletableResultCode shutdown() { return result; } + /** + * Calls {@link SdkMeterProvider#forceFlush() meterProvider.forceFlush} + * + * @return The resulting {@link CompletableResultCode} completes when all complete. + */ + public CompletableResultCode forceFlush() { + return meterProvider.forceFlush(); + } + + /** + * {@link SdkMeterProvider#forceFlush() flushes} the {@link #meterProvider} and waits for it to complete. + */ + public void flush() { + CompletableResultCode resultCode = forceFlush(); + if (!resultCode.isDone()) { + CountDownLatch latch = new CountDownLatch(1); + resultCode.whenComplete(() -> latch.countDown()); + try { + latch.await(15, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + /** * Registers {@link MeterProvider this} to {@link GlobalMeterProvider#set(MeterProvider)} */ diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index ba9443f67b..3098621d59 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -1,6 +1,5 @@ package rocks.inspectit.ocelot.core.opentelemetry; -import io.opencensus.trace.Tracing; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; import io.opentelemetry.api.common.Attributes; @@ -9,7 +8,6 @@ import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; -import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.extension.trace.propagation.B3Propagator; import io.opentelemetry.extension.trace.propagation.JaegerPropagator; import io.opentelemetry.opencensusshim.metrics.OpenCensusMetrics; @@ -21,8 +19,8 @@ import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; -import io.opentelemetry.sdk.trace.samplers.Sampler; import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -36,16 +34,15 @@ import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; +import rocks.inspectit.ocelot.core.exporter.DynamicMultiSpanExporter; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableTraceExporterService; -import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; import javax.annotation.PostConstruct; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.concurrent.TimeUnit; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -62,7 +59,7 @@ *

* The hierarchy of {@link io.opentelemetry.sdk.metrics.SdkMeterProvider} is as follows. All fields are private final and thus can only be changed via reflection. *

- * The hierarchy of {@link io.opentelemetry.sdk.metrics.SdkMeterProvider} is as follows. All fields are private final and thus can only be changed via reflection. + * The hierarchy of {@link io.opentelemetry.sdk.metrics.SdkMeterProvider} is as ffollows. All fields are private final and thus can only be changed via reflection. *

* The {@link io.opentelemetry.sdk.metrics.SdkMeterProvider} contains {@link io.opentelemetry.sdk.metrics.SdkMeterProvider#sharedState} and {@link io.opentelemetry.sdk.metrics.SdkMeterProvider#collectionInfoMap}. * The {@link io.opentelemetry.sdk.metrics.internal.state.AutoValue_MeterProviderSharedState} has nothing of interest. @@ -79,13 +76,16 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { @Getter private boolean enabled = false; + @Getter + private boolean stopped = false; + /** - * Whether something in {@link rocks.inspectit.ocelot.config.model.tracing.TracingSettings} of the {@link InspectitConfig} changed + * Whether something in {@link rocks.inspectit.ocelot.config.model.tracing.TracingSettings} or any of the {@link rocks.inspectit.ocelot.config.model.exporters.trace.TraceExportersSettings} of the {@link InspectitConfig} changed */ private boolean tracingSettingsChanged = false; /** - * Whether something in {@link rocks.inspectit.ocelot.config.model.metrics.MetricsSettings} of the {@link InspectitConfig} changed + * Whether something in {@link rocks.inspectit.ocelot.config.model.metrics.MetricsSettings} or any of the {@link rocks.inspectit.ocelot.config.model.exporters.metrics.MetricsExportersSettings} of the {@link InspectitConfig} changed */ private boolean metricSettingsChanged = false; @@ -102,48 +102,56 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { private AtomicBoolean isConfiguring = new AtomicBoolean(false); /** - * Returns whether the {@link OpenTelemetryControllerImpl} is currently (re-)configuring tracing and metrics - * - * @return Whether the {@link OpenTelemetryControllerImpl} is currently (re-)configuring tracing and metrics + * The registered {@link DynamicallyActivatableTraceExporterService}. */ - public boolean isConfiguring() { - return isConfiguring.get(); - } - - /** - * The set of registered {@link DynamicallyActivatableTraceExporterService}. - */ - private Set registeredTraceExportServices; + private Map registeredTraceExportServices = new ConcurrentHashMap<>(); /** - * The set of registered {@link DynamicallyActivatableMetricsExporterService}. + * The registered {@link DynamicallyActivatableMetricsExporterService}. */ - private Set registeredMetricExporterServices; + private Map registeredMetricExporterServices = new ConcurrentHashMap<>(); private Resource serviceNameResource; /** - * The custom {@link OpenTelemetryImpl} + * The {@link OpenTelemetryImpl} that wraps {@link OpenTelemetrySdk} */ private OpenTelemetryImpl openTelemetry; + /** + * The {@link MeterProviderImpl} that wraps {@link SdkMeterProvider} + */ private MeterProviderImpl meterProvider; @Autowired InspectitEnvironment env; - private Sampler sampler; + /** + * The {@link DynamicSampler} used for tracing + */ + private DynamicSampler sampler; - @PostConstruct - void init() { - log.info("INIT at timestamp {}", System.currentTimeMillis()); + /** + * The {@link BatchSpanProcessor} used to process all spans + */ + private SpanProcessor spanProcessor; - registeredTraceExportServices = new LinkedHashSet<>(); - registeredMetricExporterServices = new LinkedHashSet<>(); + /** + * The {@link DynamicMultiSpanExporter} wrapper that is used to forward all spans to a list of {@link io.opentelemetry.sdk.trace.export.SpanExporter} (one for each {@link DynamicallyActivatableTraceExporterService} + */ + private DynamicMultiSpanExporter spanExporter; + @PostConstruct + void init() { + // create the service name resource serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, env.getCurrentConfig() .getServiceName())); + // create span processor, exporter, and sampler + spanExporter = new DynamicMultiSpanExporter(); + spanProcessor = BatchSpanProcessor.builder(spanExporter).build(); + sampler = new DynamicSampler(env.getCurrentConfig().getTracing().getSampleProbability()); + // close the tracer provider when the JVM is shutting down Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); @@ -159,6 +167,15 @@ synchronized private void startAtStartup(ContextRefreshedEvent event) { start(); } + /** + * Returns whether the {@link OpenTelemetryControllerImpl} is currently (re-)configuring tracing and metrics + * + * @return Whether the {@link OpenTelemetryControllerImpl} is currently (re-)configuring tracing and metrics + */ + public boolean isConfiguring() { + return isConfiguring.get(); + } + /** * Configures and registers {@link io.opentelemetry.api.OpenTelemetry}, triggered by the {@link rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent} triggered * For tracing, the {@link SdkTracerProvider} is reconfigured and updated in the {@link GlobalOpenTelemetry}. @@ -185,6 +202,11 @@ synchronized private boolean configureOpenTelemetry() { // set serviceName serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, configuration.getServiceName())); + // check if the tracing sample probability changed + if (null == sampler || sampler.getSampleProbability() != configuration.getTracing() + .getSampleProbability()) { + tracingSettingsChanged = true; + } // configure tracing if not configured or when tracing settings changed boolean successConfigureTracing = !(tracingSettingsChanged || !configured) ? true : configureTracing(configuration); @@ -207,44 +229,58 @@ synchronized private boolean configureOpenTelemetry() { } + @Override + synchronized public boolean start() { + enabled = true; + // if OTEL has not been configured (since last shutdown), configure it + return configured = !configured ? configureOpenTelemetry() : true; + } + + /** + * Flushes the {@link #openTelemetry} and {@link #meterProvider} and waits for it to complete + */ + @Override + public void flush() { + openTelemetry.flush(); + meterProvider.flush(); + } + /** - * Shuts down the {@link SdkTracerProvider} set for {@link GlobalOpenTelemetry} and the {@link SdkMeterProvider} set for {@link GlobalMeterProvider} + * Shuts down the {@link OpenTelemetryControllerImpl} by calling {@link OpenTelemetryImpl#close()} and {@link MeterProviderImpl#close()} and waits for it to complete. + * The shutdown is final, i.e., once this {@link OpenTelemetryImpl} is shutdown, it cannot be re-enabled! */ @Override synchronized public void shutdown() { long start = System.nanoTime(); + // close tracing if (null != openTelemetry) { - openTelemetry.shutdown(); + // note: close calls SdkTracerProvider#shutdown, which calls SpanProcessor#shutdown, which calls SpanExporter#shutdown. + // thus, the spanProcessor and spanExporter are shut down in this process and cannot be used later + openTelemetry.close(); } + // close metric provider if (null != meterProvider) { long startMeterProviderShutdown = System.nanoTime(); - CompletableResultCode shutdownResult = meterProvider.shutdown(); - log.info("time to shut down {}: {} ms", meterProvider, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startMeterProviderShutdown)); + // note: close calls SdkMeterProvider#shutdown, which calls MetricReader#shutdown, which calls MetricExporter#shutdown + CompletableResultCode shutdownResult = meterProvider.close(); + log.info("time to shut down {}: {} ms (success={})", meterProvider, (System.nanoTime() - startMeterProviderShutdown) * 1e-6, shutdownResult.isSuccess()); } GlobalMeterProvider.set(null); GlobalOpenTelemetry.resetForTest(); configured = false; enabled = false; + stopped = true; // set all OTEL related fields to null openTelemetry = null; meterProvider = null; serviceNameResource = null; sampler = null; + spanExporter = null; + spanProcessor = null; - log.info("Shut down {}. The shutdown process took {} ms", getClass().getSimpleName(), TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)); - } - - @Override - synchronized public boolean start() { - enabled = true; - // if OTEL has not been configured (since last shutdown), configure it - if (!configured) { - return configured = configureOpenTelemetry(); - } else { - return true; - } + log.info("Shut down {}. The shutdown process took {} ms", getClass().getSimpleName(), (System.nanoTime() - start) * 1e-6); } @Override @@ -257,38 +293,127 @@ synchronized public void notifyMetricsSettingsChanged() { metricSettingsChanged = true; } - public static void configureAndRegisterDefault() { - log.info("Configure and register default OpenTelemetry"); - - // set up tracer provider - SdkTracerProvider tracerProvider = SdkTracerProvider.builder() - .addSpanProcessor(SimpleSpanProcessor.create(new LoggingSpanExporter())) - .setSampler(Sampler.alwaysOn()) - .build(); - - // build and register OTel - OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() - .setTracerProvider(tracerProvider) - .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance()))) - // W3CBaggagePropagator.getInstance() - .buildAndRegisterGlobal(); + /** + * Configures the tracing, i.e. {@link #openTelemetry} and the related {@link SdkTracerProvider}. A new {@link SdkTracerProvider} is only built once or after {@link #shutdown()} was called. + * + * @param configuration + * + * @return Whether {@link #openTelemetry} was successfully configured + */ + private synchronized boolean configureTracing(InspectitConfig configuration) { + if (!enabled) { + return true; + } + try { + + boolean buildTracerProvider = null == openTelemetry; + + // set up the sampler if necessary + double sampleProbability = configuration.getTracing().getSampleProbability(); + if (null == sampler) { + sampler = new DynamicSampler(sampleProbability); + buildTracerProvider = true; + } + // update sample probability + sampler.setSampleProbability(sampleProbability); + + // set up span processor and span exporter if necessary + if (null == spanProcessor || spanExporter == null) { + // create new span exporter if null + if (null == spanExporter) { + spanExporter = new DynamicMultiSpanExporter(); + // register the span exporters for all registered services + for (DynamicallyActivatableTraceExporterService service : registeredTraceExportServices.values()) { + spanExporter.registerSpanExporter(service.getName(), service.getSpanExporter()); + } + } + // build the span processor + spanProcessor = BatchSpanProcessor.builder(spanExporter).build(); + buildTracerProvider = true; + } + + // if the TracerProvider needs to be build, build it and register the OpenTelemetryImpl + if (buildTracerProvider) { + // build TracerProvider + SdkTracerProviderBuilder builder = SdkTracerProvider.builder() + .setSampler(sampler) + .setResource(serviceNameResource) + .addSpanProcessor(spanProcessor); + + // build the SdkTracerProvider + SdkTracerProvider tracerProvider = builder.build(); + + // build OTEL + OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) + .build(); + + // if the OpenTelemetryImpl has not been build, then build and register it + if (null == openTelemetry) { + openTelemetry = OpenTelemetryImpl.builder().openTelemetry(openTelemetrySdk).build(); + + // check if any OpenTelemetry has been registered to GlobalOpenTelemetry. + // If so, reset it. + if (null != OpenTelemetryUtils.getGlobalOpenTelemetry()) { + log.info("reset {}", GlobalOpenTelemetry.get().getClass().getName()); + GlobalOpenTelemetry.resetForTest(); + } + + // set GlobalOpenTelemetry + openTelemetry.registerGlobal(); + } + // otherwise, just update the underlying OpenTelemetrySdk + else { + openTelemetry.set(openTelemetrySdk); + } + + // update the OTEL_TRACER field in OpenTelemetrySpanBuilderImpl + OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); + } + + return true; + } catch (Exception e) { + log.error("Failed to configure OpenTelemetry Tracing", e); + return false; + } } /** - * Registers a new {@link DynamicallyActivatableService} + * Configures the {@link SdkMeterProvider} and registers it as the {@link GlobalMeterProvider} via {@link GlobalMeterProvider#set(MeterProvider)} * - * @param service + * @param configuration * * @return */ - // TODO: make accessible? discuss whether it is better to have this kind of catch-all method or rather individual register methods (see below) - private boolean registerExporterService(DynamicallyActivatableService service) { - if (service instanceof DynamicallyActivatableTraceExporterService) { - return registerTraceExporterService((DynamicallyActivatableTraceExporterService) service); - } else if (service instanceof DynamicallyActivatableMetricsExporterService) { - return registerMetricExporterService((DynamicallyActivatableMetricsExporterService) service); - } else { - log.error("Cannot register service {}. The class is not supported. Currently supported classes are {} and {}", service.getName(), DynamicallyActivatableTraceExporterService.class, DynamicallyActivatableMetricsExporterService.class); + private synchronized boolean configureMeterProvider(InspectitConfig configuration) { + if (!enabled) { + return true; + } + try { + SdkMeterProviderBuilder builder = SdkMeterProvider.builder().setResource(serviceNameResource); + + for (DynamicallyActivatableMetricsExporterService metricsExportService : registeredMetricExporterServices.values()) { + log.info("add metricReader for {} ({})", metricsExportService, metricsExportService.getNewMetricReaderFactory()); + builder.registerMetricReader(OpenCensusMetrics.attachTo(metricsExportService.getNewMetricReaderFactory())); + } + + SdkMeterProvider sdkMeterProvider = builder.build(); + + // if the MeterProvider is null, build and register it + if (null == meterProvider) { + meterProvider = MeterProviderImpl.builder().meterProvider(sdkMeterProvider).build(); + meterProvider.registerGlobal(); + } + // otherwise, just update the internally used SdkMeterProvider + else { + meterProvider.set(sdkMeterProvider); + } + + return true; + + } catch (Exception e) { + log.error("Failed to configure MeterProvider", e); return false; } } @@ -302,11 +427,23 @@ private boolean registerExporterService(DynamicallyActivatableService service) { */ public boolean registerTraceExporterService(DynamicallyActivatableTraceExporterService service) { if (null == registeredTraceExportServices) { - registeredTraceExportServices = new LinkedHashSet<>(); + registeredTraceExportServices = new ConcurrentHashMap<>(); } try { + // try to register the span exporter of the service + if (null != spanExporter) { + if (spanExporter.registerSpanExporter(service.getName(), service.getSpanExporter())) { + log.info("The spanExporter {} for the service {} was successfully registered.", service.getSpanExporter() + .getClass() + .getName(), service.getName()); + } else { + log.error("The spanExporter {} for the service {} was already registered", service.getSpanExporter() + .getClass() + .getName(), service.getName()); + } + } // try to add the service if it has not already been registered - if (registeredTraceExportServices.add(service)) { + if (null == registeredTraceExportServices.put(service.getName(), service)) { notifyTracingSettingsChanged(); log.info("The service {} was successfully registered.", service.getName()); return true; @@ -314,7 +451,6 @@ public boolean registerTraceExporterService(DynamicallyActivatableTraceExporterS log.warn("The service {} was already registered", service.getName()); return false; } - // return configureOpenTelemetry(); } catch (Exception e) { log.error("Failed to register " + service.getName(), e); return false; @@ -322,19 +458,38 @@ public boolean registerTraceExporterService(DynamicallyActivatableTraceExporterS } - public boolean unregisterTraceExporterService(DynamicallyActivatableTraceExporterService service) { - - if (registeredTraceExportServices.remove(service)) { + /** + * Unregisters a {@link DynamicallyActivatableTraceExporterService} registered under the given name + * + * @param serviceName The name of the {@link DynamicallyActivatableTraceExporterService service} + * + * @return Whether the {@link DynamicallyActivatableTraceExporterService service} was successfully unregistered. Returns false if no service with the given name was previously registered. + */ + private boolean unregisterTraceExporterService(String serviceName) { + // unregister the service by removing it from the map of registered services and from the spanExporter + // evaluates to true when a service with the given name was previously registered + if (!(null == registeredTraceExportServices.remove(serviceName) || (spanExporter != null && !spanExporter.unregisterSpanExporter(serviceName)))) { notifyTracingSettingsChanged(); return true; } else { - log.warn("Failed to unregister {}. The service has not been registered.", service.getName()); + log.warn("Failed to unregister {}. The service has not been registered.", serviceName); return false; } } /** - * Register a {@link DynamicallyActivatableMetricsExporterService} + * Unregisters a {@link DynamicallyActivatableTraceExporterService} + * + * @param service The {@link DynamicallyActivatableTraceExporterService} + * + * @return Whether the {@link DynamicallyActivatableTraceExporterService service} was successfully unregistered. Returns false if no service with the given name was previously registered. + */ + public boolean unregisterTraceExporterService(DynamicallyActivatableTraceExporterService service) { + return unregisterTraceExporterService(service.getName()); + } + + /** + * Registers a {@link DynamicallyActivatableMetricsExporterService} * * @param service * @@ -342,10 +497,10 @@ public boolean unregisterTraceExporterService(DynamicallyActivatableTraceExporte */ public boolean registerMetricExporterService(DynamicallyActivatableMetricsExporterService service) { if (null == registeredMetricExporterServices) { - registeredMetricExporterServices = new LinkedHashSet<>(); + registeredMetricExporterServices = new ConcurrentHashMap<>(); } try { - if (registeredMetricExporterServices.add(service)) { + if (null == registeredMetricExporterServices.put(service.getName(), service)) { notifyMetricsSettingsChanged(); log.info("The service {} was successfully registered.", service.getName()); return true; @@ -353,156 +508,38 @@ public boolean registerMetricExporterService(DynamicallyActivatableMetricsExport log.warn("The service {} was already registered!", service.getName()); return false; } - // return configureOpenTelemetry(); } catch (Exception e) { log.error("Failed to register " + service.getName(), e); return false; } } - public boolean unregisterMetricExporterService(DynamicallyActivatableMetricsExporterService service) { - if (registeredMetricExporterServices.remove(service)) { - notifyMetricsSettingsChanged(); - return true; - } else { - log.warn("Failed to unregister {}. The service has not been registered.", service.getName()); - return false; - } - } - - private synchronized boolean configureTracing(InspectitConfig configuration) { - if (!enabled) { - return true; - } - try { - // set up sampler - double probability = configuration.getTracing().getSampleProbability(); - sampler = Sampler.traceIdRatioBased(probability); - - // build TracerProvider - SdkTracerProviderBuilder builder = SdkTracerProvider.builder() - .setSampler(sampler) - .setResource(serviceNameResource); - - // add all SpanProcessors - for (DynamicallyActivatableTraceExporterService traceExportServices : registeredTraceExportServices) { - // TODO: or do we rather want to have a getter method in the service? - builder.addSpanProcessor(traceExportServices.getSpanProcessor()); - log.info("addSpanProcessor for service {}", traceExportServices); - } - - // add SpanProcessors that have been added by testing methods - for (SpanProcessor spanProcessor : spanProcessorsForTesting) { - builder.addSpanProcessor(spanProcessor); - } - - // build the SdkTracerProvider - SdkTracerProvider tracerProvider = builder.build(); - - // rebuild OTel - OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() - .setTracerProvider(tracerProvider) - .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) - // TODO: do I also need the W3CBaggagePropagator or B3Propagator? - // W3CBaggagePropagator.getInstance() - .build(); - - // if the OpenTelemetryImpl has not been build, then build and register it - if (null == openTelemetry) { - openTelemetry = OpenTelemetryImpl.builder().openTelemetry(openTelemetrySdk).build(); - - // check if any OpenTelemetry has been registered to GlobalOpenTelemetry. - // If so, reset it. - if (null != OpenTelemetryUtils.getGlobalOpenTelemetry()) { - log.info("reset {}", GlobalOpenTelemetry.get().getClass().getName()); - GlobalOpenTelemetry.resetForTest(); - } - - // set GlobalOpenTelemetry - openTelemetry.registerGlobal(); - } - // otherwise, just update the underlying OpenTelemetrySdk - else { - openTelemetry.set(openTelemetrySdk); - } - - // update the OTEL_TRACER field in OpenTelemetrySpanBuilderImpl - OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); - - log.info("SpanExporter={}", Tracing.getExportComponent().getSpanExporter()); - return true; - } catch (Exception e) { - log.error("Failed to configure OpenTelemetry Tracing", e); - return false; - } - } - /** - * Configures the {@link SdkMeterProvider} and registers it as the {@link GlobalMeterProvider} via {@link GlobalMeterProvider#set(MeterProvider)} + * Unregisters a {@link DynamicallyActivatableMetricsExporterService} with the given name * - * @param configuration + * @param serviceName The name of the {@link DynamicallyActivatableTraceExporterService service} * - * @return + * @return Whether the {@link DynamicallyActivatableMetricsExporterService service} was successfully unregistered. Returns false if a service with the given name was already registered and has been overwritten. */ - private synchronized boolean configureMeterProvider(InspectitConfig configuration) { - if (!enabled) { - return true; - } - try { - SdkMeterProviderBuilder builder = SdkMeterProvider.builder().setResource(serviceNameResource); - - for (DynamicallyActivatableMetricsExporterService metricsExportService : registeredMetricExporterServices) { - log.info("add metricReader for {} ({})", metricsExportService, metricsExportService.getNewMetricReaderFactory()); - builder.registerMetricReader(OpenCensusMetrics.attachTo(metricsExportService.getNewMetricReaderFactory())); - } - - SdkMeterProvider sdkMeterProvider = builder.build(); - - // if the MeterProvider is null, build and register it - if (null == meterProvider) { - meterProvider = MeterProviderImpl.builder().meterProvider(sdkMeterProvider).build(); - meterProvider.registerGlobal(); - } - // otherwise, just update the internally used SdkMeterProvider - else { - meterProvider.set(sdkMeterProvider); - } - + private boolean unregisterMetricExporterService(String serviceName) { + if (null != registeredMetricExporterServices.remove(serviceName)) { + notifyMetricsSettingsChanged(); return true; - - } catch (Exception e) { - log.error("Failed to configure MeterProvider", e); + } else { + log.warn("Failed to unregister {}. The service has not been registered.", serviceName); return false; } } /** - * List of {@link SpanProcessor}. - */ - Set spanProcessorsForTesting = new LinkedHashSet<>(); - - /** - * Registers a {@link SpanProcessor}. ONLY USE THIS METHOD FOR TESTING! + * Unregisters a {@link DynamicallyActivatableMetricsExporterService} * - * @param spanProcessor + * @param service The {@link DynamicallyActivatableMetricsExporterService} to unregister * - * @return Whether {@link #configureTracing(InspectitConfig)} was successful, i.e., {@link GlobalOpenTelemetry} was updated + * @return Whether the {@link DynamicallyActivatableMetricsExporterService service} was successfully registered. Returns false if the service was already registered. */ - public boolean registerSpanProcessorForTesting(SpanProcessor spanProcessor) { - spanProcessorsForTesting.add(spanProcessor); - return configureTracing(env.getCurrentConfig()); + public boolean unregisterMetricExporterService(DynamicallyActivatableMetricsExporterService service) { + return unregisterMetricExporterService(service.getName()); } - /** - * Unregisters a {@link SpanProcessor}. ONLY USE THIS METHOD FOR TESTING - * - * @param spanProcessor - * - * @return Whether {@link #configureTracing(InspectitConfig)} was successful, i.e., {@link GlobalOpenTelemetry} was updated - */ - - public boolean unregisterSpanProcessorForTesting(SpanProcessor spanProcessor) { - spanProcessorsForTesting.remove(spanProcessor); - return configureTracing(env.getCurrentConfig()); - } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java index a4293e22b6..a2fa28bf02 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java @@ -10,6 +10,9 @@ import lombok.extern.slf4j.Slf4j; import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + /** * Custom implementation of {@link OpenTelemetry} that wraps {@link OpenTelemetrySdk}. * This wrapper class is used to change {@link #openTelemetry} without the exception handling of {@link io.opentelemetry.api.GlobalOpenTelemetry#set(OpenTelemetry)} @@ -66,7 +69,7 @@ public void set(OpenTelemetrySdk openTelemetry) { * * @return The {@link CompletableResultCode} */ - public synchronized CompletableResultCode shutdown() { + public synchronized CompletableResultCode close() { CompletableResultCode result; synchronized (lock) { result = OpenTelemetryUtils.stopTracerProvider(openTelemetry.getSdkTracerProvider(), true); @@ -74,6 +77,22 @@ public synchronized CompletableResultCode shutdown() { return result; } + /** + * {@link io.opentelemetry.sdk.trace.SdkTracerProvider#forceFlush() flushes} the {@link #openTelemetry} and waits for it to complete. + */ + public void flush() { + CompletableResultCode resultCode = openTelemetry.getSdkTracerProvider().forceFlush(); + if (!resultCode.isDone()) { + CountDownLatch latch = new CountDownLatch(1); + resultCode.whenComplete(() -> latch.countDown()); + try { + latch.await(15, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + /** * Registers {@link OpenTelemetryImpl this} to {@link GlobalOpenTelemetry#set(OpenTelemetry)} * diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/service/DynamicallyActivatableService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/service/DynamicallyActivatableService.java index faefa47745..8833e17323 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/service/DynamicallyActivatableService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/service/DynamicallyActivatableService.java @@ -34,7 +34,7 @@ public abstract class DynamicallyActivatableService { @Autowired protected OpenTelemetryControllerImpl openTelemetryController; - + private List configDependencies; /** @@ -162,9 +162,10 @@ synchronized void checkForUpdates(InspectitConfigChangedEvent ev) { /** * Gets the name of the {@link DynamicallyActivatableService} + * * @return The name of the {@link DynamicallyActivatableService} */ - public String getName(){ - return getClass().getName(); + public String getName() { + return getClass().getSimpleName(); } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java index 2327ff643f..13c99c7a95 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java @@ -48,14 +48,14 @@ public static CompletableResultCode stopMeterProvider(SdkMeterProvider meterProv flushResult.whenComplete(() -> latch.countDown()); try { latch.await(10, TimeUnit.SECONDS); - log.info("time to force flush SdkMeterProvider: {} ms", (System.nanoTime() - startFlush) / 1E6); + log.info("time to force flush SdkMeterProvider: {} ms", (System.nanoTime() - startFlush) * 1e-6); } catch (Throwable t) { log.error("failed to force flush SdkMeterProvider", t); t.printStackTrace(); return CompletableResultCode.ofFailure(); } } else { - log.info("time to force flush SdkMeterProvider: {} ms", (System.nanoTime() - startFlush) / 1E6); + log.info("time to force flush SdkMeterProvider: {} ms", (System.nanoTime() - startFlush) * 1e-6); } } @@ -66,7 +66,7 @@ public static CompletableResultCode stopMeterProvider(SdkMeterProvider meterProv } /** - * Stops the given {@link SdkTracerProvider} and blocks waiting for it to complete. + * {@link SdkTracerProvider#close() Closes} the given {@link SdkTracerProvider} and blocks waiting for it to complete. * * @param tracerProvider The {@link SdkTracerProvider} to shut down */ @@ -96,7 +96,7 @@ public static CompletableResultCode stopTracerProvider(SdkTracerProvider tracerP e.printStackTrace(); return CompletableResultCode.ofFailure(); } - log.info("time to force flush {}: {} ms", tracerProvider, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startFlush)); + log.info("time to force flush {}: {} ms", tracerProvider, (System.nanoTime() - startFlush) * 1e-6); } } tracerProvider.close(); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java index 94ddf72b95..8b5c11dfca 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java @@ -14,8 +14,8 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; +import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.core.SpringTestBase; -import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; import java.util.concurrent.TimeUnit; @@ -81,7 +81,7 @@ private Tracer getTracer() { return GlobalOpenTelemetry.getTracer(INSTRUMENTATION_NAME, INSTRUMENTATION_VERSION); } - private void makeSpans() { + private void makeSpansAndFlush() { // start span and nested span Span parentSpan = getTracer().spanBuilder("openTelemetryParentSpan").startSpan(); try (Scope scope = parentSpan.makeCurrent()) { @@ -94,6 +94,9 @@ private void makeSpans() { } finally { parentSpan.end(); } + + // flush pending spans + Instances.openTelemetryController.flush(); } @DirtiesContext @@ -101,7 +104,7 @@ private void makeSpans() { void verifyOpenTelemetryTraceSent() throws InterruptedException { assertThat(service.isEnabled()).isTrue(); - makeSpans(); + makeSpansAndFlush(); Awaitility.waitAtMost(10, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { // assert that two traces have been logged @@ -129,7 +132,8 @@ void verifyOpenTelemetryTraceSent() throws InterruptedException { localSwitch(true); Awaitility.waitAtMost(1, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); - makeSpans(); + makeSpansAndFlush(); + // wait until the new spans are exported to the log Awaitility.waitAtMost(10, TimeUnit.SECONDS) .pollInterval(2, TimeUnit.SECONDS) @@ -147,7 +151,7 @@ void testLoggingExporterDisabled() throws InterruptedException { assertThat(service.isEnabled()).isFalse(); - makeSpans(); + makeSpansAndFlush(); // make sure that no spans were exported assertThat(spanLogs.getEvents()).hasSize(0); @@ -158,31 +162,13 @@ void testLoggingExporterDisabled() throws InterruptedException { @Nested class OpenCensusLogging { - @Test - void testTracerRestart() { - Tracer tracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); - // turn of the service - localSwitch(false); - Awaitility.waitAtMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isFalse()); - // turn the service on again - localSwitch(true); - Awaitility.waitAtMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); - - //OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); - - // make sure the new tracer is different - Tracer newTracer = OpenCensusShimUtils.getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl(); - assertThat(tracer).isNotSameAs(newTracer); - - } - @DirtiesContext @Test void verifyOpenCensusTraceSent() throws InterruptedException { assertThat(service.isEnabled()).isTrue(); // make some spans - makeSpans(); + makeSpansAndFlush(); // assert that both spans are logged assertThat(spanLogs.getEvents()).hasSize(2); @@ -201,7 +187,7 @@ void verifyOpenCensusTraceSent() throws InterruptedException { Awaitility.waitAtMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); // make spans - makeSpans(); + makeSpansAndFlush(); // verify logging of spans Awaitility.waitAtMost(10, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) @@ -212,7 +198,7 @@ private io.opencensus.trace.Tracer getTracer() { return Tracing.getTracer(); } - private void makeSpans() { + private void makeSpansAndFlush() { // get OC tracer and start spans io.opencensus.trace.Tracer tracer = getTracer(); @@ -223,6 +209,9 @@ private void makeSpans() { span.addAnnotation("invoking stuff in openCensusChild"); } } + + // flush pending spans + Instances.openTelemetryController.flush(); } } From 108cb31a6d0a2148bfcaee738cb0be030e90a686 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 13 Jan 2022 11:14:49 +0100 Subject: [PATCH 43/86] #1269: Added tests to test tracing configuration in OpenTelemetryControllerImpl --- .../OpenTelemetryControllerImpl.java | 33 +++- .../OpenTelemetryControllerImplTest.java | 177 ++++++++++++++++++ 2 files changed, 202 insertions(+), 8 deletions(-) create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index 3098621d59..9c581ea31e 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -1,5 +1,6 @@ package rocks.inspectit.ocelot.core.opentelemetry; +import com.google.common.annotations.VisibleForTesting; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; import io.opentelemetry.api.common.Attributes; @@ -22,7 +23,9 @@ import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import lombok.AccessLevel; import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.ContextRefreshedEvent; @@ -104,18 +107,24 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { /** * The registered {@link DynamicallyActivatableTraceExporterService}. */ - private Map registeredTraceExportServices = new ConcurrentHashMap<>(); + @VisibleForTesting + @Getter(AccessLevel.PACKAGE) + Map registeredTraceExportServices = new ConcurrentHashMap<>(); /** * The registered {@link DynamicallyActivatableMetricsExporterService}. */ - private Map registeredMetricExporterServices = new ConcurrentHashMap<>(); + @VisibleForTesting + @Getter(AccessLevel.PACKAGE) + Map registeredMetricExporterServices = new ConcurrentHashMap<>(); private Resource serviceNameResource; /** * The {@link OpenTelemetryImpl} that wraps {@link OpenTelemetrySdk} */ + @VisibleForTesting + @Getter(AccessLevel.PACKAGE) private OpenTelemetryImpl openTelemetry; /** @@ -139,9 +148,12 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { /** * The {@link DynamicMultiSpanExporter} wrapper that is used to forward all spans to a list of {@link io.opentelemetry.sdk.trace.export.SpanExporter} (one for each {@link DynamicallyActivatableTraceExporterService} */ + @VisibleForTesting + @Setter(AccessLevel.PACKAGE) private DynamicMultiSpanExporter spanExporter; @PostConstruct + @VisibleForTesting void init() { // create the service name resource serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, env.getCurrentConfig() @@ -185,8 +197,9 @@ public boolean isConfiguring() { */ @EventListener(InspectitConfigChangedEvent.class) @Order(Ordered.LOWEST_PRECEDENCE) + @VisibleForTesting // make sure this is called after the individual services have (un)-registered - synchronized private boolean configureOpenTelemetry() { + synchronized boolean configureOpenTelemetry() { log.info("configureOpenTelemetry at timestamp {}", System.currentTimeMillis()); boolean success = false; if (!isConfiguring.compareAndSet(false, true)) { @@ -213,12 +226,14 @@ synchronized private boolean configureOpenTelemetry() { // configure meter provider (metrics) if not configured or when metrics settings changed boolean successConfigureMeterProvider = !(metricSettingsChanged || !configured) ? true : configureMeterProvider(configuration); - if (successConfigureTracing && successConfigureMeterProvider) { + success = successConfigureTracing && successConfigureMeterProvider; + + if (success) { log.info("Successfully configured OpenTelemetry with TracerProvider and MeterProvider"); } else { log.error("Failed to configure OpenTelemetry. Please scan the logs for detailed failure messages."); } - success = successConfigureTracing && successConfigureMeterProvider; + } isConfiguring.set(false); @@ -300,7 +315,8 @@ synchronized public void notifyMetricsSettingsChanged() { * * @return Whether {@link #openTelemetry} was successfully configured */ - private synchronized boolean configureTracing(InspectitConfig configuration) { + @VisibleForTesting + synchronized boolean configureTracing(InspectitConfig configuration) { if (!enabled) { return true; } @@ -386,7 +402,8 @@ private synchronized boolean configureTracing(InspectitConfig configuration) { * * @return */ - private synchronized boolean configureMeterProvider(InspectitConfig configuration) { + @VisibleForTesting + synchronized boolean configureMeterProvider(InspectitConfig configuration) { if (!enabled) { return true; } @@ -468,7 +485,7 @@ public boolean registerTraceExporterService(DynamicallyActivatableTraceExporterS private boolean unregisterTraceExporterService(String serviceName) { // unregister the service by removing it from the map of registered services and from the spanExporter // evaluates to true when a service with the given name was previously registered - if (!(null == registeredTraceExportServices.remove(serviceName) || (spanExporter != null && !spanExporter.unregisterSpanExporter(serviceName)))) { + if (null != registeredTraceExportServices.remove(serviceName) & (spanExporter == null || !spanExporter.unregisterSpanExporter(serviceName))) { notifyTracingSettingsChanged(); return true; } else { diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java new file mode 100644 index 0000000000..88f6ee06e3 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java @@ -0,0 +1,177 @@ +package rocks.inspectit.ocelot.core.opentelemetry; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.TracerProvider; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.annotation.DirtiesContext; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.core.config.InspectitEnvironment; +import rocks.inspectit.ocelot.core.exporter.DynamicMultiSpanExporter; +import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableTraceExporterService; + +import java.lang.reflect.Method; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +/** + * Test class for {@link OpenTelemetryControllerImpl} + */ +@ExtendWith(MockitoExtension.class) +class OpenTelemetryControllerImplTest { + + @Spy + OpenTelemetryControllerImpl openTelemetryController = new OpenTelemetryControllerImpl(); + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + InspectitEnvironment env; + + @BeforeEach + void initOpenTelemetryController() { + openTelemetryController.env = env; + openTelemetryController.init(); + openTelemetryController.start(); + } + + @Nested + class ConfigureTracing { + + @Spy + DynamicMultiSpanExporter spanExporter; + + @Spy + TestTraceService testTraceService = new TestTraceService(); + + @BeforeEach + protected void init() { + openTelemetryController.setSpanExporter(spanExporter); + clearInvocations(openTelemetryController); + } + + /** + * Registers the {@link #testTraceService} to the {@link #openTelemetryController} and verifies the interactions + * + * @param expected Whether the registration is expected to succeed. + */ + protected void registerTestTraceExporterServiceAndVerify(boolean expected) { + int registeredServices = openTelemetryController.registeredTraceExportServices.size(); + assertThat(openTelemetryController.registerTraceExporterService(testTraceService)).isEqualTo(expected); + assertThat(openTelemetryController.registeredTraceExportServices.size()).isEqualTo(registeredServices + 1); + verify(openTelemetryController, times(1)).registerTraceExporterService(testTraceService); + verify(openTelemetryController, times(expected ? 1 : 0)).notifyTracingSettingsChanged(); + verify(spanExporter, times(1)).registerSpanExporter(testTraceService.getName(), testTraceService.getSpanExporter()); + verifyNoMoreInteractions(openTelemetryController, spanExporter); + clearInvocations(openTelemetryController, spanExporter); + } + + /** + * Unregisters the {@link #testTraceService} from the {@link #openTelemetryController} and verifies the interactions + * + * @param expected Whether the unregistration is expected to succeed. + */ + protected void unregisterTestTraceExporterServiceAndVerify(boolean expected) { + int registeredServices = openTelemetryController.registeredTraceExportServices.size(); + assertThat(openTelemetryController.unregisterTraceExporterService(testTraceService)).isEqualTo(expected); + assertThat(openTelemetryController.registeredTraceExportServices.size()).isEqualTo(registeredServices - 1); + verify(openTelemetryController, times(1)).unregisterTraceExporterService(testTraceService); + verify(openTelemetryController, times(expected ? 1 : 0)).notifyTracingSettingsChanged(); + verify(spanExporter, times(1)).unregisterSpanExporter(testTraceService.getName()); + verifyNoMoreInteractions(openTelemetryController, spanExporter); + clearInvocations(openTelemetryController, spanExporter); + } + + @DirtiesContext + @Test + void testTraceExporterServiceRegistration() { + + // check that service is successfully registered + registerTestTraceExporterServiceAndVerify(true); + + // service cannot be registered twice + registerTestTraceExporterServiceAndVerify(false); + } + + @Test + void testTraceExporterServiceUnregistration() { + // when a service is not registered, unregistration fails + unregisterTestTraceExporterServiceAndVerify(false); + + // register service + registerTestTraceExporterServiceAndVerify(true); + + // unregistering should now succeed + unregisterTestTraceExporterServiceAndVerify(false); + } + + @Test + void testConfigureTracing() { + TracerProvider tracerProvider = GlobalOpenTelemetry.getTracerProvider(); + // register test service + registerTestTraceExporterServiceAndVerify(true); + // configure OTEL + assertThat(openTelemetryController.configureOpenTelemetry()).isTrue(); + // tracing should have changed but metrics not + verify(openTelemetryController, times(1)).configureTracing(any(InspectitConfig.class)); + verify(openTelemetryController, times(0)).configureMeterProvider(any(InspectitConfig.class)); + + // verify that the tracer provider does not change after tracing has been (re-)configured + assertThat(tracerProvider).isEqualTo(tracerProvider); + } + + @Test + void testShutdown() { + openTelemetryController.shutdown(); + assertThat(openTelemetryController.isStopped()); + assertThat(OpenTelemetry.noop() == GlobalOpenTelemetry.get()); + } + + } + + /** + * A noop {@link DynamicallyActivatableTraceExporterService} for testing + */ + static class TestTraceService extends DynamicallyActivatableTraceExporterService { + + @Override + public SpanExporter getSpanExporter() { + try { + Method m = Class.forName("io.opentelemetry.sdk.trace.export.NoopSpanExporter") + .getDeclaredMethod("getInstance"); + m.setAccessible(true); + return (SpanExporter) m.invoke(null); + } catch (Throwable t) { + t.printStackTrace(); + return null; + } + } + + @Override + protected boolean checkEnabledForConfig(InspectitConfig configuration) { + return false; + } + + @Override + protected boolean doEnable(InspectitConfig configuration) { + return true; + } + + @Override + protected boolean doDisable() { + return true; + } + + @Override + public String getName() { + return getClass().getSimpleName(); + } + } +} \ No newline at end of file From e4c3fd30d46dcb77164814284a223f607dd7869c Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 13 Jan 2022 11:15:23 +0100 Subject: [PATCH 44/86] #1269: Fixed initialization of OpenTelemetry in Log4J2TraceIdAutoInjectorTest --- .../logging/Log4J2TraceIdAutoInjectorTest.java | 7 ++++--- .../java/rocks/inspectit/ocelot/utils/TestUtils.java | 12 ++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java index 774ba6a2e6..3f8cf805b9 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java @@ -8,6 +8,7 @@ import org.apache.logging.log4j.message.MessageFactory; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.instrumentation.InstrumentationSysTestBase; import rocks.inspectit.ocelot.logging.Log4J2LoggingRecorder; import rocks.inspectit.ocelot.utils.TestUtils; @@ -23,15 +24,15 @@ public static void waitForInstrumentation() { TestUtils.waitForClassInstrumentations(AbstractMessageFactory.class, MessageFactory.class); } - // TODO: maybe remove this method once the OTelController has been implemented @BeforeAll public static void initializeOpenTelemetry() { - // initialize OTel as we are otherwise not exporting any traces. - TestUtils.initializeOpenTelemetryForSystemTesting(); + TestUtils.waitForOpenTelemetryControllerInitialization(); } @Test public void logStringAndTraceExists() { + System.out.println(String.format("OTEL=%s, tracer=%s", Instances.openTelemetryController.getClass() + .getSimpleName(), Tracing.getTracer().getClass().getSimpleName())); Log4J2LoggingRecorder.loggingEvents.clear(); String message = "This is a traced String in {}."; String traceId; diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java index 7da8cdd7a1..311c523885 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java @@ -29,6 +29,7 @@ import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import io.opentelemetry.sdk.trace.samplers.Sampler; import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import org.awaitility.Awaitility; import org.awaitility.core.ConditionTimeoutException; import rocks.inspectit.ocelot.bootstrap.AgentManager; import rocks.inspectit.ocelot.bootstrap.Instances; @@ -235,6 +236,17 @@ public static void waitForInstrumentationToComplete() { }); } + /** + * Waits for the initialization of {@link rocks.inspectit.ocelot.core.opentelemetry.OpenTelemetryControllerImpl}, which is then registered to {@link Instances#openTelemetryController} + */ + public static void waitForOpenTelemetryControllerInitialization() { + Awaitility.await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(NoopOpenTelemetryController.INSTANCE != Instances.openTelemetryController).isTrue(); + assertThat(Instances.openTelemetryController.isEnabled()).isTrue(); + assertThat(Instances.openTelemetryController.isConfigured()).isTrue(); + }); + } + public static Map getCurrentTagsAsMap() { Map result = new HashMap<>(); InternalUtils.getTags(Tags.getTagger().getCurrentTagContext()) From 7f20156c1695ea0eeb6b23929ee495d550af21c2 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 13 Jan 2022 13:57:58 +0100 Subject: [PATCH 45/86] #1269: Added tests to test metrics (meter provider) configuration in OpenTelemetryControllerImpl --- .../core/opentelemetry/MeterProviderImpl.java | 15 +- .../OpenTelemetryControllerImplTest.java | 166 ++++++++++++++++-- 2 files changed, 158 insertions(+), 23 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java index 14c1b42877..0234fd8c60 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java @@ -30,7 +30,8 @@ public class MeterProviderImpl implements MeterProvider { private Object lock = new Object(); /** - * Registers the {@link SdkMeterProvider}. If an instance of {@link SdkMeterProvider} was already registered, it is {@link SdkMeterProvider#forceFlush() flushed} and {@link SdkMeterProvider#shutdown() shutdown} and blocks waiting for it before the new {@link SdkMeterProvider} is set to {@link #meterProvider} + * Registers the {@link SdkMeterProvider}. + * If an instance of {@link SdkMeterProvider} was already registered, it is {@link SdkMeterProvider#forceFlush() flushed} and {@link SdkMeterProvider#shutdown() shutdown} and blocks waiting for it to complete before the new {@link SdkMeterProvider} is set to {@link #meterProvider} * * @param meterProvider */ @@ -61,9 +62,13 @@ public SdkMeterProvider get() { * @return */ public CompletableResultCode close() { - CompletableResultCode result; - synchronized (lock) { - result = OpenTelemetryUtils.stopMeterProvider(meterProvider, true); + CompletableResultCode result = new CompletableResultCode(); + if (null != meterProvider) { + synchronized (lock) { + result = OpenTelemetryUtils.stopMeterProvider(meterProvider, true); + } + } else { + result.succeed(); } return result; } @@ -74,7 +79,7 @@ public CompletableResultCode close() { * @return The resulting {@link CompletableResultCode} completes when all complete. */ public CompletableResultCode forceFlush() { - return meterProvider.forceFlush(); + return meterProvider != null ? meterProvider.forceFlush() : CompletableResultCode.ofSuccess(); } /** diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java index 88f6ee06e3..79d2457fd3 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java @@ -2,7 +2,13 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.GlobalMeterProvider; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.trace.TracerProvider; +import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.trace.export.SpanExporter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -15,7 +21,7 @@ import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; -import rocks.inspectit.ocelot.core.exporter.DynamicMultiSpanExporter; +import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableTraceExporterService; import java.lang.reflect.Method; @@ -40,6 +46,13 @@ void initOpenTelemetryController() { openTelemetryController.env = env; openTelemetryController.init(); openTelemetryController.start(); + clearInvocations(openTelemetryController); + } + + @Test + void testShutdown() { + openTelemetryController.shutdown(); + assertThat(openTelemetryController.isStopped()); } @Nested @@ -49,7 +62,7 @@ class ConfigureTracing { DynamicMultiSpanExporter spanExporter; @Spy - TestTraceService testTraceService = new TestTraceService(); + TestTraceExporterService testTraceExporterService; @BeforeEach protected void init() { @@ -58,40 +71,40 @@ protected void init() { } /** - * Registers the {@link #testTraceService} to the {@link #openTelemetryController} and verifies the interactions + * Registers the {@link #testTraceExporterService} to the {@link #openTelemetryController} and verifies the interactions * * @param expected Whether the registration is expected to succeed. */ protected void registerTestTraceExporterServiceAndVerify(boolean expected) { int registeredServices = openTelemetryController.registeredTraceExportServices.size(); - assertThat(openTelemetryController.registerTraceExporterService(testTraceService)).isEqualTo(expected); - assertThat(openTelemetryController.registeredTraceExportServices.size()).isEqualTo(registeredServices + 1); - verify(openTelemetryController, times(1)).registerTraceExporterService(testTraceService); + assertThat(openTelemetryController.registerTraceExporterService(testTraceExporterService)).isEqualTo(expected); + assertThat(openTelemetryController.registeredTraceExportServices.size()).isEqualTo(registeredServices + (expected ? 1 : 0)); + verify(openTelemetryController, times(1)).registerTraceExporterService(testTraceExporterService); verify(openTelemetryController, times(expected ? 1 : 0)).notifyTracingSettingsChanged(); - verify(spanExporter, times(1)).registerSpanExporter(testTraceService.getName(), testTraceService.getSpanExporter()); + verify(spanExporter, times(1)).registerSpanExporter(testTraceExporterService.getName(), testTraceExporterService.getSpanExporter()); verifyNoMoreInteractions(openTelemetryController, spanExporter); clearInvocations(openTelemetryController, spanExporter); } /** - * Unregisters the {@link #testTraceService} from the {@link #openTelemetryController} and verifies the interactions + * Unregisters the {@link #testTraceExporterService} from the {@link #openTelemetryController} and verifies the interactions * * @param expected Whether the unregistration is expected to succeed. */ protected void unregisterTestTraceExporterServiceAndVerify(boolean expected) { int registeredServices = openTelemetryController.registeredTraceExportServices.size(); - assertThat(openTelemetryController.unregisterTraceExporterService(testTraceService)).isEqualTo(expected); - assertThat(openTelemetryController.registeredTraceExportServices.size()).isEqualTo(registeredServices - 1); - verify(openTelemetryController, times(1)).unregisterTraceExporterService(testTraceService); + assertThat(openTelemetryController.unregisterTraceExporterService(testTraceExporterService)).isEqualTo(expected); + assertThat(openTelemetryController.registeredTraceExportServices.size()).isEqualTo(registeredServices - (expected ? 1 : 0)); + verify(openTelemetryController, times(1)).unregisterTraceExporterService(testTraceExporterService); verify(openTelemetryController, times(expected ? 1 : 0)).notifyTracingSettingsChanged(); - verify(spanExporter, times(1)).unregisterSpanExporter(testTraceService.getName()); + verify(spanExporter, times(1)).unregisterSpanExporter(testTraceExporterService.getName()); verifyNoMoreInteractions(openTelemetryController, spanExporter); clearInvocations(openTelemetryController, spanExporter); } @DirtiesContext @Test - void testTraceExporterServiceRegistration() { + void testRegisterTraceExporterService() { // check that service is successfully registered registerTestTraceExporterServiceAndVerify(true); @@ -101,15 +114,16 @@ void testTraceExporterServiceRegistration() { } @Test - void testTraceExporterServiceUnregistration() { + void testUnregisterTraceExporterService() { // when a service is not registered, unregistration fails unregisterTestTraceExporterServiceAndVerify(false); + System.out.println(testTraceExporterService.getName() + " - " + openTelemetryController.registeredTraceExportServices.size()); // register service registerTestTraceExporterServiceAndVerify(true); - + System.out.println(openTelemetryController.registeredTraceExportServices.size()); // unregistering should now succeed - unregisterTestTraceExporterServiceAndVerify(false); + unregisterTestTraceExporterServiceAndVerify(true); } @Test @@ -130,16 +144,97 @@ void testConfigureTracing() { @Test void testShutdown() { openTelemetryController.shutdown(); - assertThat(openTelemetryController.isStopped()); assertThat(OpenTelemetry.noop() == GlobalOpenTelemetry.get()); } } + @Nested + class ConfigureMeterProvider { + + @Spy + TestMetricsExporterService testMetricsExporterService; + + /** + * Registers the {@link #testMetricsExporterService} to the {@link #openTelemetryController} and verifies the interactions + * + * @param expected Whether the registration is expected to succeed. + */ + private void registerTestMetricExporterServiceAndVerify(boolean expected) { + int numRegisteredServices = openTelemetryController.registeredMetricExporterServices.size(); + assertThat(openTelemetryController.registerMetricExporterService(testMetricsExporterService)).isEqualTo(expected); + assertThat(openTelemetryController.registeredMetricExporterServices.size()).isEqualTo(numRegisteredServices + (expected ? 1 : 0)); + verify(openTelemetryController, times(1)).registerMetricExporterService(testMetricsExporterService); + verify(openTelemetryController, times(expected ? 1 : 0)).notifyMetricsSettingsChanged(); + clearInvocations(openTelemetryController); + } + + /** + * Unregisters the {@link #testMetricsExporterService} from the {@link #openTelemetryController} and verifies the interactions + * + * @param expected Whether the unregistration is expected to succeed. + */ + private void unregisterTestMetricExporterServiceAndVerify(boolean expected) { + int numRegisteredServices = openTelemetryController.registeredMetricExporterServices.size(); + assertThat(openTelemetryController.unregisterMetricExporterService(testMetricsExporterService)).isEqualTo(expected); + assertThat(openTelemetryController.registeredMetricExporterServices.size()).isEqualTo(numRegisteredServices - (expected ? 1 : 0)); + verify(openTelemetryController, times(1)).unregisterMetricExporterService(testMetricsExporterService); + verify(openTelemetryController, times(expected ? 1 : 0)).notifyMetricsSettingsChanged(); + clearInvocations(openTelemetryController); + } + + @Test + void testRegisterMetricsExporterService() { + // first registration succeeds + registerTestMetricExporterServiceAndVerify(true); + + // second should fail as the service was already registered + registerTestMetricExporterServiceAndVerify(false); + } + + @Test + void testUnregisterMetricsExporterService() { + // when a service is not registered, unregistration fails + unregisterTestMetricExporterServiceAndVerify(false); + + // register service + registerTestMetricExporterServiceAndVerify(true); + + // unregistration should succeed + unregisterTestMetricExporterServiceAndVerify(true); + } + + @Test + void testConfigureMetrics() { + MeterProviderImpl meterProviderImpl = openTelemetryController.getMeterProvider(); + + MeterProvider globalMeterProvider = GlobalMeterProvider.get(); + // register some service + registerTestMetricExporterServiceAndVerify(true); + // configure OTEL + assertThat(openTelemetryController.configureOpenTelemetry()).isTrue(); + + // verify that meter provider was configured but not tracing + verify(openTelemetryController, times(1)).configureMeterProvider(any(InspectitConfig.class)); + verify(openTelemetryController, times(0)).configureTracing(any(InspectitConfig.class)); + + // (global) meter provider should not have changed during configuration + assertThat(openTelemetryController.getMeterProvider()).isSameAs(meterProviderImpl); + assertThat(GlobalMeterProvider.get()).isSameAs(globalMeterProvider); + + } + + @Test + void testShutdown() { + openTelemetryController.shutdown(); + assertThat(null == GlobalMeterProvider.get()); + } + } + /** * A noop {@link DynamicallyActivatableTraceExporterService} for testing */ - static class TestTraceService extends DynamicallyActivatableTraceExporterService { + static class TestTraceExporterService extends DynamicallyActivatableTraceExporterService { @Override public SpanExporter getSpanExporter() { @@ -174,4 +269,39 @@ public String getName() { return getClass().getSimpleName(); } } + + /** + * A noop {@link DynamicallyActivatableMetricsExporterService} for testing + */ + static class TestMetricsExporterService extends DynamicallyActivatableMetricsExporterService { + + @Mock + MetricExporter metricExporter; + + @Override + public MetricReaderFactory getNewMetricReaderFactory() { + return PeriodicMetricReader.newMetricReaderFactory(new LoggingMetricExporter()); + } + + @Override + protected boolean checkEnabledForConfig(InspectitConfig configuration) { + return false; + } + + @Override + protected boolean doEnable(InspectitConfig configuration) { + return false; + } + + @Override + protected boolean doDisable() { + return false; + } + + @Override + public String getName() { + return "test-metrics-exporter-service"; + } + } + } \ No newline at end of file From ffe57962eac595b46f38f8bbd34c128703d673e3 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 13 Jan 2022 13:58:14 +0100 Subject: [PATCH 46/86] #1269: Moved DynamicMultiSpanExporter --- .../DynamicMultiSpanExporter.java | 2 +- .../core/opentelemetry/OpenTelemetryControllerImpl.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) rename inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/{exporter => opentelemetry}/DynamicMultiSpanExporter.java (98%) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicMultiSpanExporter.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/DynamicMultiSpanExporter.java similarity index 98% rename from inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicMultiSpanExporter.java rename to inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/DynamicMultiSpanExporter.java index 8469cbe78b..8e7191f39c 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicMultiSpanExporter.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/DynamicMultiSpanExporter.java @@ -1,4 +1,4 @@ -package rocks.inspectit.ocelot.core.exporter; +package rocks.inspectit.ocelot.core.opentelemetry; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.trace.SpanProcessor; diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index 9c581ea31e..0858d24702 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -37,7 +37,6 @@ import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; -import rocks.inspectit.ocelot.core.exporter.DynamicMultiSpanExporter; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableTraceExporterService; import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; @@ -130,6 +129,8 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { /** * The {@link MeterProviderImpl} that wraps {@link SdkMeterProvider} */ + @VisibleForTesting + @Getter(AccessLevel.PACKAGE) private MeterProviderImpl meterProvider; @Autowired @@ -485,7 +486,7 @@ public boolean registerTraceExporterService(DynamicallyActivatableTraceExporterS private boolean unregisterTraceExporterService(String serviceName) { // unregister the service by removing it from the map of registered services and from the spanExporter // evaluates to true when a service with the given name was previously registered - if (null != registeredTraceExportServices.remove(serviceName) & (spanExporter == null || !spanExporter.unregisterSpanExporter(serviceName))) { + if (null != registeredTraceExportServices.remove(serviceName) & (spanExporter == null || spanExporter.unregisterSpanExporter(serviceName))) { notifyTracingSettingsChanged(); return true; } else { From 1ed77447e763dba2fa726a5f0cd335016c8a7493 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 17 Jan 2022 09:55:31 +0100 Subject: [PATCH 47/86] #1269: Removed OpenCensusAgentMetricsExporterSettings.java and OpenCensusAgentTraceExporterSettings.java and updated related files --- ...penCensusAgentMetricsExporterSettings.java | 41 ------------------- .../OpenCensusAgentTraceExporterSettings.java | 35 ---------------- .../ocelot/config/default/exporters.yml | 27 ------------ 3 files changed, 103 deletions(-) delete mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OpenCensusAgentMetricsExporterSettings.java delete mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OpenCensusAgentTraceExporterSettings.java diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OpenCensusAgentMetricsExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OpenCensusAgentMetricsExporterSettings.java deleted file mode 100644 index 2e04c05278..0000000000 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OpenCensusAgentMetricsExporterSettings.java +++ /dev/null @@ -1,41 +0,0 @@ -package rocks.inspectit.ocelot.config.model.exporters.metrics; - -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.validation.constraints.NotNull; -import java.time.Duration; - -@Data -@NoArgsConstructor -public class OpenCensusAgentMetricsExporterSettings { - - private boolean enabled; - - /** - * The address of the OpenCensus Agent. - */ - private String address; - - /** - * Disable SSL - */ - private boolean useInsecure; - - /** - * The service name under which traces are published, defaults to inspectit.service-name: - */ - private String serviceName; - - /** - * Defines the reconnection period in seconds - */ - @NotNull - private Duration reconnectionPeriod; - - /** - * Defines the export interval in seconds - */ - @NotNull - private Duration exportInterval; -} \ No newline at end of file diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OpenCensusAgentTraceExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OpenCensusAgentTraceExporterSettings.java deleted file mode 100644 index 396c0ac06a..0000000000 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OpenCensusAgentTraceExporterSettings.java +++ /dev/null @@ -1,35 +0,0 @@ -package rocks.inspectit.ocelot.config.model.exporters.trace; - -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.validation.constraints.NotNull; -import java.time.Duration; - -@Data -@NoArgsConstructor -public class OpenCensusAgentTraceExporterSettings { - - private boolean enabled; - - /** - * The address of the OpenCensus Agent. - */ - private String address; - - /** - * Disable SSL - */ - private boolean useInsecure; - - /** - * The service name under which traces are published, defaults to inspectit.service-name: - */ - private String serviceName; - - /** - * Defines the reconnection period in seconds - */ - @NotNull - private Duration reconnectionPeriod; -} \ No newline at end of file diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml index ac8ff5cd35..aafe9b5e55 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml @@ -15,20 +15,6 @@ inspectit: # the port on which the /metrics endpoint of prometheus will be started port: 8888 - # settings for the OpenCensus Agent Metrics exporter (https://opencensus.io/exporters/supported-exporters/java/ocagent/) - open-census-agent: - # if true, the agent will try to start the OpenCensus agent metrics exporter - enabled: true - # address of the open-census agent (e.g. localhost:1234) - address: null - # if true, SSL is disabled - use-insecure: false - # the service-name which will be used to publish the metrics - service-name: ${inspectit.service-name} - # the time at which the exporter tries to reconnect to the OpenCensus agent - reconnection-period: 5s - # the export interval of the metrics - export-interval: ${inspectit.metrics.frequency} influx: enabled: true @@ -77,19 +63,6 @@ inspectit: # the service-name which will be used to publish the spans service-name: ${inspectit.service-name} - # settings for the OpenCensus Agent Trace exporter (https://opencensus.io/exporters/supported-exporters/java/ocagent/) - open-census-agent: - # if true, the agent will try to start the OpenCensus agent trace exporter - enabled: true - # Address of the open-census agent (e.g. localhost:1234) - address: null - # if true, SSL is disabled - use-insecure: false - # the service-name which will be used to publish the spans - service-name: ${inspectit.service-name} - # the time at which the exporter tries to reconnect to the OpenCensus agent - reconnection-period: 5s - # settings for the LoggingSpanExporter used in LoggingTraceExporterService logging: enabled: true From 2f408acb69e23185492b2fb1450af2c14e37ff9c Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 17 Jan 2022 12:02:41 +0100 Subject: [PATCH 48/86] #1269: Ad: Removed OpenCensusAgentMetricsExporterSettings.java and OpenCensusAgentTraceExporterSettings.java and updated related files --- .../metrics/MetricsExportersSettings.java | 3 -- .../trace/TraceExportersSettings.java | 3 -- .../core/exporter/InfluxExporterService.java | 36 ++++++++++++++----- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java index 11ef356dae..2f5f10b7c8 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java @@ -15,9 +15,6 @@ public class MetricsExportersSettings { @Valid private PrometheusExporterSettings prometheus; - @Valid - private OpenCensusAgentMetricsExporterSettings openCensusAgent; - @Valid private InfluxExporterSettings influx; diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java index d054d51d0f..b87e1eff57 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java @@ -15,9 +15,6 @@ public class TraceExportersSettings { @Valid private ZipkinExporterSettings zipkin; - @Valid - private OpenCensusAgentTraceExporterSettings openCensusAgent; - @Valid private LoggingTraceExporterSettings logging; diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterService.java index 19dc1e85e1..0c77a652f7 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterService.java @@ -13,6 +13,7 @@ import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; /** * Uses the {@link InfluxExporter} to directly push metrics into a given InfluxDB version 1.x . @@ -21,6 +22,18 @@ @Component public class InfluxExporterService extends DynamicallyActivatableService { + /** + * Dummy user for downwards compatibility + */ + public final static String DUMMY_USER = "user"; + + /** + * Dummy password for downwards compatibility + */ + public final static String DUMMY_PASSWORD = "password"; + + private static Logger LOGGER = Logger.getLogger(InfluxExporterService.class.getName()); + @Autowired private ScheduledExecutorService executor; @@ -44,24 +57,29 @@ public InfluxExporterService() { @Override protected boolean checkEnabledForConfig(InspectitConfig conf) { InfluxExporterSettings influx = conf.getExporters().getMetrics().getInflux(); - return conf.getMetrics().isEnabled() - && influx.isEnabled() - && !StringUtils.isEmpty(influx.getUrl()) - && !StringUtils.isEmpty(influx.getDatabase()) - && !StringUtils.isEmpty(influx.getRetentionPolicy()); + return conf.getMetrics() + .isEnabled() && influx.isEnabled() && !StringUtils.isEmpty(influx.getUrl()) && !StringUtils.isEmpty(influx.getDatabase()) && !StringUtils.isEmpty(influx.getRetentionPolicy()); } @Override protected boolean doEnable(InspectitConfig configuration) { InfluxExporterSettings influx = configuration.getExporters().getMetrics().getInflux(); - log.info("Starting InfluxDB Exporter to '{}:{}' on '{}'", influx.getDatabase(), influx.getRetentionPolicy(), influx - .getUrl()); + String user = influx.getUser(); + String password = influx.getPassword(); + + // check user and password, which are not allowed to be null as of v1.15.0 + if (null == user) { + user = DUMMY_USER; + password = DUMMY_PASSWORD; + LOGGER.warning(String.format("You are using the InfluxDB exporter without specifying 'user' and 'password'. Since v1.15.0, 'user' and 'password' are mandatory. Will be using the dummy user '%s' and dummy password '%s'.", DUMMY_USER, DUMMY_PASSWORD)); + } + log.info("Starting InfluxDB Exporter to '{}:{}' on '{}'", influx.getDatabase(), influx.getRetentionPolicy(), influx.getUrl()); activeExporter = InfluxExporter.builder() .url(influx.getUrl()) .database(influx.getDatabase()) .retention(influx.getRetentionPolicy()) - .user(influx.getUser()) - .password(influx.getPassword()) + .user(user) + .password(password) .createDatabase(influx.isCreateDatabase()) .exportDifference(influx.isCountersAsDifferences()) .measurementNameProvider(percentileViewManager::getMeasureNameForSeries) From 062632653cca5477e1c136fe36496dc3ea0a8e7e Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 17 Jan 2022 12:03:14 +0100 Subject: [PATCH 49/86] #1269: Added new properties to TracingSettings (considering batch span exporting) --- .../config/model/tracing/TracingSettings.java | 19 +++++++++++++++---- .../docs/tracing/tracing.md | 10 ++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java index fe5d36dcd9..a884cece87 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java @@ -7,6 +7,7 @@ import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; +import java.util.concurrent.TimeUnit; @Data @NoArgsConstructor @@ -16,10 +17,7 @@ public class TracingSettings { * Enum that defines when are common tags added to span attributes. */ public enum AddCommonTags { - NEVER, - ON_GLOBAL_ROOT, - ON_LOCAL_ROOT, - ALWAYS + NEVER, ON_GLOBAL_ROOT, ON_LOCAL_ROOT, ALWAYS } /** @@ -61,4 +59,17 @@ public enum AddCommonTags { */ @NotNull private PropagationFormat propagationFormat = PropagationFormat.B3; + + static final int DEFAULT_MAX_EXPORT_BATCH_SIZE = 512; + + /** + * The maximum batch size for every span export. Default value is 512. + */ + private int maxExportBatchSize = DEFAULT_MAX_EXPORT_BATCH_SIZE; + + static final long DEFAULT_SCHEDULE_DELAY_MILLIS = 5000; + /** + * Delay interval between two consecutive exports in milliseconds. Default value is 5000ms. + */ + private long scheduleDelayMillis = DEFAULT_SCHEDULE_DELAY_MILLIS; } diff --git a/inspectit-ocelot-documentation/docs/tracing/tracing.md b/inspectit-ocelot-documentation/docs/tracing/tracing.md index b59637bbe6..bcf57a16ff 100644 --- a/inspectit-ocelot-documentation/docs/tracing/tracing.md +++ b/inspectit-ocelot-documentation/docs/tracing/tracing.md @@ -21,6 +21,16 @@ You can configure the probability with which a trace ends up being collected via E.g. setting the value to `0.1` will result in only 10% of all traces being collected. By default, the sample probability is 100%. Note that this global setting only acts as a default value and can be overridden by [individual rules](instrumentation/rules.md#collecting-traces). +### Additional Properties + +You can additionally define the following global properties (`inspectit.tracing-property`) + +|Property|Default|Description| +|---|---|---| +|`max-export-batch-size`|512|The max export batch size for every export, i.e., the maximum number of spans exported by the used `BatchSpanProcessor`| +|`schedule-delay-millis`|5000|The delay interval between two consecutive exports in milliseconds. +**Note**: These properties take only effect once when the agent is starting. If you change these properties while the agent is running, they will not take effect until the agent retarted. + ### Common Tags as Attributes Globally defined [common tags](metrics/common-tags.md) used when recording metrics can also be inserted as attributes in traces. From 4d0ad5460c94c987b8cbcd6fadfd0a2894cf83ce Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 17 Jan 2022 13:23:53 +0100 Subject: [PATCH 50/86] #1269: Removed the serviceName property from JaegerExporterSettings, ZipkinExporterSettings, LoggingTraceExporterSettings, and LoggingMetricsExporterSettings, and updated related documentation. --- .../exporters/metrics/InfluxExporterSettings.java | 3 +++ .../metrics/LoggingMetricsExporterSettings.java | 2 -- .../exporters/trace/JaegerExporterSettings.java | 5 ----- .../trace/LoggingTraceExporterSettings.java | 2 -- .../exporters/trace/ZipkinExporterSettings.java | 5 ----- .../inspectit/ocelot/config/default/exporters.yml | 15 +++++---------- inspectit-ocelot-core/build.gradle | 3 --- .../core/exporter/OtlpTraceExporterService.java | 3 ++- .../docs/metrics/metric-exporters.md | 15 ++++++--------- .../docs/tracing/trace-exporters.md | 15 ++++----------- 10 files changed, 20 insertions(+), 48 deletions(-) diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/InfluxExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/InfluxExporterSettings.java index af7ab23a63..a5d2daa915 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/InfluxExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/InfluxExporterSettings.java @@ -6,6 +6,7 @@ import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; import java.time.Duration; +import java.util.logging.Logger; /** * Settings for the InfluxDB metrics exporter. @@ -13,6 +14,8 @@ @Data public class InfluxExporterSettings { + private static Logger LOGGER = Logger.getLogger(InfluxExporterSettings.class.getName()); + /** * If true, the influx exporter will be started (if the DB is not null) */ diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/LoggingMetricsExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/LoggingMetricsExporterSettings.java index cc46109970..34847e4f21 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/LoggingMetricsExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/LoggingMetricsExporterSettings.java @@ -15,8 +15,6 @@ public class LoggingMetricsExporterSettings { private boolean enabled; - private String serviceName; - /** * Defines how often metrics are pushed to the log. */ diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java index 905de069c5..a57de44004 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java @@ -20,9 +20,4 @@ public class JaegerExporterSettings { */ private String grpc; - /** - * The service name under which traces are published, defaults to inspectit.service-name; - */ - private String serviceName; - } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java index c59ce169d0..396435c1d5 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java @@ -12,6 +12,4 @@ public class LoggingTraceExporterSettings { private boolean enabled; - private String serviceName; - } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/ZipkinExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/ZipkinExporterSettings.java index 19535476e5..8f6c2d9e94 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/ZipkinExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/ZipkinExporterSettings.java @@ -14,9 +14,4 @@ public class ZipkinExporterSettings { */ private String url; - /** - * The service name under which traces are published, defaults to inspectit.service-name; - */ - private String serviceName; - } diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml index aafe9b5e55..27c91a82c8 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml @@ -37,8 +37,7 @@ inspectit: # settings for the LoggingMetricExporter used in LoggingExporterService logging: - enabled: true - serviceName: ${inspectit.service-name} + enabled: false # the export interval of the metrics export-interval: ${inspectit.metrics.frequency} @@ -51,25 +50,21 @@ inspectit: enabled: true # the v2 Url under which the ZipKin server can be accessed, e.g. http://127.0.0.1:9411/api/v2/spans url: null - # the service-name which will be used to publish the spans - service-name: ${inspectit.service-name} - # settings for the jaeger exporter (https://github.com/census-instrumentation/opencensus-java/tree/master/exporters/trace/jaeger) + + # settings for the jaeger exporter (https://github.com/census-instrumentation/opencensus-java/tree/master/exporters/trace/jaeger) jaeger: # if true, the agent will try to start the Jaeger trace exporter enabled: true # the URL under which the jaeger thrift server can be accessed, e.g. http://127.0.0.1:14268/api/traces url: null - # the service-name which will be used to publish the spans - service-name: ${inspectit.service-name} + # settings for the LoggingSpanExporter used in LoggingTraceExporterService logging: - enabled: true - service-name: ${inspectit.service-name} + enabled: false # settings for the OtlpGrpcSpanExporter used in OtlpTraceExporterService otlp: enabled: true - service-name: ${inspectit.service-name} url: null diff --git a/inspectit-ocelot-core/build.gradle b/inspectit-ocelot-core/build.gradle index b9e7994ac0..27f226ea05 100644 --- a/inspectit-ocelot-core/build.gradle +++ b/inspectit-ocelot-core/build.gradle @@ -13,9 +13,6 @@ test { jvmArgs '-Xmx512m', '-XX:+HeapDumpOnOutOfMemoryError', '-XX:HeapDumpPath=/__w/inspectit-ocelot/inspectit-ocelot/test_heapdump.bin' - // include custom configurations to potentially speed up tests or change settings in test files - jvmArgs "-Dinspectit.config.file-based.path=$projectDir/src/test/resources/config" - testLogging { exceptionFormat = 'full' } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java index 864f463251..cfa949dc07 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java @@ -1,6 +1,7 @@ package rocks.inspectit.ocelot.core.exporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.sdk.trace.export.SpanExporter; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -19,7 +20,7 @@ public class OtlpTraceExporterService extends DynamicallyActivatableTraceExporterService { @Getter - private OtlpGrpcSpanExporter spanExporter; + private SpanExporter spanExporter; public OtlpTraceExporterService() { super("exporters.tracing.otlp", "tracing.enabled"); diff --git a/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md b/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md index faf8abddaa..aa3243843a 100644 --- a/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md +++ b/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md @@ -14,12 +14,11 @@ inspectIT Ocelot currently supports the following OpenTelemetry metrics exporter |Exporter |Supports run-time updates| Push / Pull |Enabled by default |---|---|---|---| -|[Logging Exporter](#logging-exporter)|Yes|Pull|Yes -|[~~Prometheus Exporter~~](#prometheus-exporter)|Yes|Pull|Yes -|[~~InfluxDB Exporter~~](#influxdb-exporter)|Yes|Push|Yes +|[Logging Exporter](#logging-exporter)|Yes|Pull|No +|[Prometheus Exporter](#prometheus-exporter)|Yes|Pull|No +|[InfluxDB Exporter](#influxdb-exporter)|Yes|Push|Yes ->**Important note**: Starting with version `1.15.0`, inspectIT Ocelot moved from OpenCensus to OpenTelemetry. As a result, the `OpenCensus Agent Exporter` is no longer supported. -Currently, Prometheus and InfluxDB are **not** functional and will be re-implemented in the next version. +>**Important note**: Starting with version `1.15.0`, inspectIT Ocelot moved from OpenCensus to OpenTelemetry. As a result, the `OpenCensus Agent Exporter` is no longer supported and has been removed. ## Logging Exporter @@ -31,7 +30,6 @@ The Logging exporter exports the metrics to the console. By default, the exporte |`.export-interval`|refers to `inspectit.metrics.frequency`|The export interval of the metrics. ## Prometheus Exporter ->**Important**: the Prometheus exporter is currently not working Prometheus exporter exposes the metrics in Prometheus format and is the default metrics exporter set up by inspectIT Ocelot. When enabled, inspectIT starts a Prometheus HTTP server in parallel with your application. @@ -49,7 +47,6 @@ The following properties are nested properties below the `inspectit.exporters.me > Don't forget to check [the official OpenTelemetry Prometheus exporter documentation](https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/prometheus). ## InfluxDB Exporter ->**Important**: the InfluxDB exporter is currently not working If enabled, metrics are pushed at a specified interval directly to a given InfluxDB v1.x instance. To enable the InfluxDB Exporters, it is only required to specify the `url`. @@ -68,8 +65,8 @@ The following properties are nested properties below the `inspectit.exporters.me |---|---|---| |`.enabled`|`true`|If true, the agent will try to start the Influx exporter, if also the `url` is not empty. |`.url`|`null`|The HTTP url of the InfluxDB, e.g. `http://localhost:8086`. -|`.user`|`null`| The user to use for connecting to the InfluxDB, can be empty if the InfluxDB is configured for unauthorized access. -|`.password`|`null`|The password to use for connecting to the InfluxDB, can be empty if the InfluxDB is configured for unauthorized access. +|`.user`|`null`| The user to use for connecting to the InfluxDB, can not be empty. +|`.password`|`null`|The password to use for connecting to the InfluxDB, can not be empty. |`.database`|`inspectit`| The InfluxDB database to which the metrics are pushed. |`.retention-policy`|`autogen`| The retention policy of the database to use for writing metrics. |`.create-database`|`true`| If enabled, the database defined by the `database` property is automatically created on startup with an `autogen` retention policy if it does not exist yet. diff --git a/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md b/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md index 526aeff9ee..c2b0ead967 100644 --- a/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md +++ b/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md @@ -8,18 +8,17 @@ Metrics exporters are responsible for passing the recorded tracing data to a cor inspectIT Ocelot currently supports the following OpenCensus trace exporters: * [Logging](#logging-exporter) [[Homepage](https://github.com/open-telemetry/opentelemetry-java/blob/main/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/LoggingSpanExporter.java)] -* [~~Zipkin~~](#zipkin-exporter) [[Homepage](https://zipkin.io/)] -* [~~Jaeger~~](#jaeger-exporter) [[Homepage](https://www.jaegertracing.io/)] +* [Zipkin](#zipkin-exporter) [[Homepage](https://zipkin.io/)] +* [Jaeger](#jaeger-exporter) [[Homepage](https://www.jaegertracing.io/)] ->**Important note**: Starting with version `1.15.0`, inspectIT Ocelot moved from OpenCensus to OpenTelemetry. As a result, the `OpenCensus Agent Exporter` is no longer supported. -Currently, Zipkin and Jaeger are **not** functional and will be re-implemented in the next version. +>**Important note**: Starting with version `1.15.0`, inspectIT Ocelot moved from OpenCensus to OpenTelemetry. As a result, the `OpenCensus Agent Exporter` is no longer supported and has been removed. +> Additionally, with OpenTelemetry, inspectIT Ocelot does not support the `service-name` property for individual exporter services anymore. Thus, we removed the `service-name` property from the Jaeger and Zipkin exporter. Please use the global `inspectit.service-name` property instead. ## Logging Exporter The Logging exporter exports traces to the system log. By default, the Logging exporter is enabled. ## Zipkin Exporter ->**Important**: the Zipkin exporter is currently not working The Zipkin exporter exports Traces in Zipkin v2 format to a Zipkin server or other compatible servers. It can be enabled and disabled via the `inspectit.exporters.tracing.zipkin.enabled` property. By default, the Zipkin exporter is enabled. It however does not have an URL configured. The exporter will start up as soon as you define the `inspectit.exporters.tracing.zipkin.url` property. @@ -30,21 +29,15 @@ For example, when adding the following property to your `-javaagent` options, tr -Dinspectit.exporters.tracing.zipkin.url=http://127.0.0.1:9411/api/v2/spans ``` -When sending spans, Zipkin expects you to give a name of the service where the spans have been recorded. This name can be set using the `inspectit.exporters.tracing.zipkin.service-name` property. This property defaults to `inspectit.service-name`. - - ## Jaeger Exporter ->**Important**: the Jaeger exporter is currently not working The Jaeger exports works exactly the same way as the [Zipkin Exporter](#zipkin-exporter). The corresponding properties are the following: * `inspectit.exporters.tracing.jaeger.enabled`: enables / disables the Jaeger exporter * `inspectit.exporters.tracing.jaeger.url`: defines the URL where the spans will be pushed -* `inspectit.exporters.tracing.jaeger.service-name`: defines the service name under which the spans will be published By default, the Jaeger exporter is enabled but has no URL configured. -The service name defaults to `inspectit.service-name`. To make inspectIT Ocelot push the spans to a Jaeger server running on the same machine as the agent, the following JVM property can be used: From f07db85ad5e26a3742ba3641e4fc11a7d9f14996 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 17 Jan 2022 13:32:38 +0100 Subject: [PATCH 51/86] #1269: Removed the SpringTestBase.yml and moved the properties to SpringTestBase.java instead. --- .../java/rocks/inspectit/ocelot/core/SpringTestBase.java | 5 +++++ .../src/test/resources/config/SpringTestBase.yml | 6 ------ 2 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 inspectit-ocelot-core/src/test/resources/config/SpringTestBase.yml diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/SpringTestBase.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/SpringTestBase.java index abb52bb98f..f98b45646d 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/SpringTestBase.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/SpringTestBase.java @@ -16,7 +16,9 @@ import org.springframework.core.env.PropertySource; import org.springframework.core.env.StandardEnvironment; import org.springframework.mock.env.MockPropertySource; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; @@ -34,6 +36,9 @@ /** * Base class for all tests. */ + +@TestPropertySource(properties = "inspectit.exporters.metrics.prometheus.enabled=false") +@DirtiesContext @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = SpringConfiguration.class, initializers = SpringTestBase.TestContextInitializer.class) public class SpringTestBase { diff --git a/inspectit-ocelot-core/src/test/resources/config/SpringTestBase.yml b/inspectit-ocelot-core/src/test/resources/config/SpringTestBase.yml deleted file mode 100644 index 2dee82f4b8..0000000000 --- a/inspectit-ocelot-core/src/test/resources/config/SpringTestBase.yml +++ /dev/null @@ -1,6 +0,0 @@ -inspectit: - exporters: - metrics: - # disable prometheus to speed up the shutdown process of OpenTelemetryControllerImpl - prometheus: - enabled: false \ No newline at end of file From 8a826dce5760b6e6b673488cd8e3560cd11cdaec Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 17 Jan 2022 13:33:13 +0100 Subject: [PATCH 52/86] #1269: Fixed InfluxExporterServiceIntTest --- .../InfluxExporterServiceIntTest.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java index d49c7823c5..3e4cb3f8f3 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java @@ -8,6 +8,7 @@ import io.opencensus.tags.TagKey; import io.opencensus.tags.TagValue; import io.opencensus.tags.Tags; +import org.influxdb.InfluxDB; import org.influxdb.InfluxDBFactory; import org.influxdb.dto.Query; import org.influxdb.dto.QueryResult; @@ -37,11 +38,9 @@ public class InfluxExporterServiceIntTest extends SpringTestBase { void startInfluxDB() throws Exception { InfluxServer.Builder builder = new InfluxServer.Builder(); int freeHttpPort = Network.getFreeServerPort(); - InfluxConfigurationWriter influxConfig = new InfluxConfigurationWriter.Builder() - .setHttp(freeHttpPort) // by default auth is disabled + InfluxConfigurationWriter influxConfig = new InfluxConfigurationWriter.Builder().setHttp(freeHttpPort) // by default auth is disabled .build(); builder.setInfluxConfiguration(influxConfig); - influx = builder.build(); influx.start(); url = "http://localhost:" + freeHttpPort; @@ -52,12 +51,20 @@ void shutdownInfluxDB() throws Exception { influx.cleanup(); } + private final String user = "w00t"; + + private final String password = "password"; + @Test void verifyInfluxDataWritten() { updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.influx.enabled", true); props.setProperty("inspectit.exporters.metrics.influx.export-interval", "1s"); props.setProperty("inspectit.exporters.metrics.influx.url", url); props.setProperty("inspectit.exporters.metrics.influx.database", DATABASE); + // note: user and password are mandatory as of v1.15.0 + props.setProperty("inspectit.exporters.metrics.influx.user", user); + props.setProperty("inspectit.exporters.metrics.influx.password", password); }); TagKey testTag = TagKey.create("my_tag"); @@ -76,20 +83,18 @@ void verifyInfluxDataWritten() { } await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { - QueryResult result = InfluxDBFactory.connect(url).query(new Query("SELECT LAST(cool_data) FROM " + DATABASE + ".autogen.my_test_measure GROUP BY *")); + InfluxDB iDB = InfluxDBFactory.connect(url, user, password); // note: user and password are mandatory as of v1.15.0 + QueryResult result = iDB.query(new Query("SELECT LAST(cool_data) FROM " + DATABASE + ".autogen.my_test_measure GROUP BY *")); List results = result.getResults(); assertThat(results).hasSize(1); QueryResult.Result data = results.get(0); assertThat(data.getSeries()).hasSize(1); QueryResult.Series series = data.getSeries().get(0); - assertThat(series.getTags()) - .hasSize(1) - .containsEntry("my_tag", "myval"); + assertThat(series.getTags()).hasSize(1).containsEntry("my_tag", "myval"); assertThat(series.getValues().get(0).get(1)).isEqualTo(42.0); }); } - } From 6e555bd110252124b6b772d442c2432860030ee1 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 17 Jan 2022 13:35:30 +0100 Subject: [PATCH 53/86] #1269: Minor refactoring (removed unnecessary logs, added TestPropertySource to tracing exporter tests) --- .../ocelot/core/opentelemetry/MeterProviderImpl.java | 1 - .../ocelot/core/opentelemetry/OpenTelemetryImpl.java | 1 - .../inspectit/ocelot/core/utils/OpenTelemetryUtils.java | 6 ------ .../ocelot/core/exporter/JaegerExporterServiceIntTest.java | 2 +- .../core/exporter/LoggingTraceExporterServiceIntTest.java | 5 ++++- .../core/exporter/PrometheusExporterServiceIntTest.java | 1 - .../ocelot/core/exporter/ZipkinExporterServiceIntTest.java | 2 +- 7 files changed, 6 insertions(+), 12 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java index 0234fd8c60..f0b5f095ba 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java @@ -39,7 +39,6 @@ public void set(SdkMeterProvider meterProvider) { synchronized (lock) { // shut down previous meterProvider if set if (null != this.meterProvider && this.meterProvider != meterProvider) { - log.info("Set new SdkMeterProvider. Shut down previous ({})", meterProvider); OpenTelemetryUtils.stopMeterProvider(this.meterProvider, true); } // set SdkMeterProvider diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java index a2fa28bf02..ebdfb4d70f 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java @@ -56,7 +56,6 @@ public ContextPropagators getPropagators() { public void set(OpenTelemetrySdk openTelemetry) { synchronized (lock) { if (null != openTelemetry) { - log.info("Set new OpenTelemetry. Shut down previous ({})", openTelemetry.getClass()); // stop previous SdkTracerProvider OpenTelemetryUtils.stopTracerProvider(this.openTelemetry.getSdkTracerProvider()); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java index 13c99c7a95..7d07479d9e 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java @@ -48,20 +48,16 @@ public static CompletableResultCode stopMeterProvider(SdkMeterProvider meterProv flushResult.whenComplete(() -> latch.countDown()); try { latch.await(10, TimeUnit.SECONDS); - log.info("time to force flush SdkMeterProvider: {} ms", (System.nanoTime() - startFlush) * 1e-6); } catch (Throwable t) { log.error("failed to force flush SdkMeterProvider", t); t.printStackTrace(); return CompletableResultCode.ofFailure(); } - } else { - log.info("time to force flush SdkMeterProvider: {} ms", (System.nanoTime() - startFlush) * 1e-6); } } // close the SdkMeterProvider. This calls shutDown internally and waits blocking for it. meterProvider.close(); - log.info("time to stop {}: {} ms", meterProvider, (System.nanoTime() - start) / 1E6); return CompletableResultCode.ofSuccess(); } @@ -96,11 +92,9 @@ public static CompletableResultCode stopTracerProvider(SdkTracerProvider tracerP e.printStackTrace(); return CompletableResultCode.ofFailure(); } - log.info("time to force flush {}: {} ms", tracerProvider, (System.nanoTime() - startFlush) * 1e-6); } } tracerProvider.close(); - log.info("time to stop {}: {} ms", tracerProvider, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)); } return CompletableResultCode.ofSuccess(); } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java index 9f302a476c..c869fb4fde 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java @@ -19,7 +19,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.awaitility.Awaitility.await; -@TestPropertySource(properties = {"inspectit.exporters.tracing.jaeger.url=http://127.0.0.1:14268/api/traces", "inspectit.exporters.tracing.jaeger.grpc=http://127.0.0.1:14267/api/traces"}) +@TestPropertySource(properties = {"inspectit.exporters.tracing.jaeger.url=http://127.0.0.1:14268/api/traces", "inspectit.exporters.tracing.jaeger.grpc=http://127.0.0.1:14267/api/traces","inspectit.tracing.max-export-batch-size=1"}) @DirtiesContext public class JaegerExporterServiceIntTest extends SpringTestBase { diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java index 8b5c11dfca..ac5e4b3f86 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java @@ -14,9 +14,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.core.SpringTestBase; -import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; import java.util.concurrent.TimeUnit; @@ -25,6 +25,8 @@ /** * Test class for {@link LoggingTraceExporterService} */ +@TestPropertySource(properties = "inspectit.tracing.max-export-batch-size:2") +@DirtiesContext public class LoggingTraceExporterServiceIntTest extends SpringTestBase { public static final String INSTRUMENTATION_NAME = "rocks.inspectit.ocelot.instrumentation"; @@ -37,6 +39,7 @@ public class LoggingTraceExporterServiceIntTest extends SpringTestBase { @Autowired LoggingTraceExporterService service; + @DirtiesContext @BeforeEach void enableService() { localSwitch(true); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java index 20d271617b..650f1aafb2 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java @@ -20,7 +20,6 @@ import static org.assertj.core.api.Assertions.catchThrowable; @TestPropertySource(properties = {"inspecit.exporters.metrics.prometheus.enabled=true"}) - @DirtiesContext public class PrometheusExporterServiceIntTest extends SpringTestBase { diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java index 74af00f51b..d4109a9d49 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java @@ -19,7 +19,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.awaitility.Awaitility.await; -@TestPropertySource(properties = {"inspectit.exporters.tracing.zipkin.url=http://127.0.0.1:9411/api/v2/spans"}) +@TestPropertySource(properties = {"inspectit.exporters.tracing.zipkin.url=http://127.0.0.1:9411/api/v2/spans", "inspectit.tracing.max-export-batch-size=1"}) @DirtiesContext public class ZipkinExporterServiceIntTest extends SpringTestBase { From 00991243e27789265bd04e4bc0252561a5bd1c1e Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 17 Jan 2022 13:38:25 +0100 Subject: [PATCH 54/86] #1269: Slimmed down OpenTelemetryControllerImpl --- .../OpenTelemetryControllerImpl.java | 180 ++++++++---------- .../OpenTelemetryControllerImplTest.java | 131 ++++++------- 2 files changed, 145 insertions(+), 166 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index 0858d24702..12b86352d7 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -21,7 +21,6 @@ import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import lombok.AccessLevel; import lombok.Getter; @@ -45,30 +44,13 @@ import javax.annotation.PostConstruct; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * The implementation of {@link IOpenTelemetryController}. The {@link OpenTelemetryControllerImpl} configures {@link GlobalOpenTelemetry} (tracing) and {@link GlobalMeterProvider} (metrics). - *

- * The hierarchy of {@link OpenTelemetrySdk} is as follows. All fields are private final and thus can only be changed via reflection - *

- * The {@link OpenTelemetrySdk} contains the {@link SdkTracerProvider}, i.e., {@link OpenTelemetrySdk#getSdkTracerProvider()}. - * The {@link SdkTracerProvider} contains the {@link io.opentelemetry.sdk.trace.TracerSharedState}, i.e., {@link SdkTracerProvider#sharedState}. - * The {@link io.opentelemetry.sdk.trace.TracerSharedState} contains {@link io.opentelemetry.sdk.trace.TracerSharedState#activeSpanProcessor}, which can be a list of {@link SpanProcessor} (for example {@link SimpleSpanProcessor} or {@link io.opentelemetry.sdk.trace.export.BatchSpanProcessor}. - *

- * The {@link SimpleSpanProcessor} contains the {@link SimpleSpanProcessor#spanExporter}, i.e., {@link io.opentelemetry.sdk.trace.export.SpanExporte}. - * The {@link io.opentelemetry.sdk.trace.export.BatchSpanProcessor} contains the {@link io.opentelemetry.sdk.trace.export.BatchSpanProcessor#worker}, which then contains the {@link io.opentelemetry.sdk.trace.export.BatchSpanProcessor.Worker#spanExporter} - *

- * The hierarchy of {@link io.opentelemetry.sdk.metrics.SdkMeterProvider} is as follows. All fields are private final and thus can only be changed via reflection. - *

- * The hierarchy of {@link io.opentelemetry.sdk.metrics.SdkMeterProvider} is as ffollows. All fields are private final and thus can only be changed via reflection. - *

- * The {@link io.opentelemetry.sdk.metrics.SdkMeterProvider} contains {@link io.opentelemetry.sdk.metrics.SdkMeterProvider#sharedState} and {@link io.opentelemetry.sdk.metrics.SdkMeterProvider#collectionInfoMap}. - * The {@link io.opentelemetry.sdk.metrics.internal.state.AutoValue_MeterProviderSharedState} has nothing of interest. - * The {@link io.opentelemetry.sdk.metrics.internal.export.AutoValue_CollectionInfo} contains the {@link io.opentelemetry.sdk.metrics.internal.export.AutoValue_CollectionInfo#reader}, i.e., {@link io.opencensus.exporter.metrics.util.MetricReader}. - * The {@link io.opentelemetry.sdk.metrics.export.MetricReaderFactory} can be implemented as {@link io.opentelemetry.sdk.metrics.export.PeriodicMetricReader}. - * The {@link io.opentelemetry.sdk.metrics.export.PeriodicMetricReader} contains {@link io.opentelemetry.sdk.metrics.export.PeriodicMetricReader#exporter}, e.g., {@link io.opentelemetry.exporter.logging.LoggingMetricExporter}, and the {@link io.opentelemetry.sdk.metrics.export.PeriodicMetricReader#scheduledFuture}. - * The {@link java.util.concurrent.ScheduledFuture} + * The individual {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService services}, i.e., {@link DynamicallyActivatableMetricsExporterService} and {@link DynamicallyActivatableTraceExporterService}, register to and unregister from {@link OpenTelemetryControllerImpl this}. + * Important note: {@link #shutdown() shutting down} the {@link OpenTelemetryControllerImpl} is final and cannot be revoked. */ @Slf4j public class OpenTelemetryControllerImpl implements IOpenTelemetryController { @@ -103,6 +85,11 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { */ private AtomicBoolean isConfiguring = new AtomicBoolean(false); + /** + * Whether the {@link OpenTelemetryImpl} is currently {@link #shutdown() shutting down} + */ + private AtomicBoolean isShuttingDown = new AtomicBoolean(false); + /** * The registered {@link DynamicallyActivatableTraceExporterService}. */ @@ -160,10 +147,7 @@ void init() { serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, env.getCurrentConfig() .getServiceName())); - // create span processor, exporter, and sampler - spanExporter = new DynamicMultiSpanExporter(); - spanProcessor = BatchSpanProcessor.builder(spanExporter).build(); - sampler = new DynamicSampler(env.getCurrentConfig().getTracing().getSampleProbability()); + initTracing(env.getCurrentConfig()); // close the tracer provider when the JVM is shutting down Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); @@ -201,7 +185,9 @@ public boolean isConfiguring() { @VisibleForTesting // make sure this is called after the individual services have (un)-registered synchronized boolean configureOpenTelemetry() { - log.info("configureOpenTelemetry at timestamp {}", System.currentTimeMillis()); + if (stopped) { + return false; + } boolean success = false; if (!isConfiguring.compareAndSet(false, true)) { log.info("Multiple configure calls"); @@ -211,8 +197,6 @@ synchronized boolean configureOpenTelemetry() { InspectitConfig configuration = env.getCurrentConfig(); - // TODO: somehow compute whether anything has changed in tracing or metrics. If no changes happened, we do not need to reconfigure tracing and metrics! - // set serviceName serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, configuration.getServiceName())); @@ -230,7 +214,7 @@ synchronized boolean configureOpenTelemetry() { success = successConfigureTracing && successConfigureMeterProvider; if (success) { - log.info("Successfully configured OpenTelemetry with TracerProvider and MeterProvider"); + log.info("Successfully configured OpenTelemetry with tracing and metrics"); } else { log.error("Failed to configure OpenTelemetry. Please scan the logs for detailed failure messages."); } @@ -242,7 +226,6 @@ synchronized boolean configureOpenTelemetry() { tracingSettingsChanged = false; metricSettingsChanged = false; return success; - } @Override @@ -264,9 +247,17 @@ public void flush() { /** * Shuts down the {@link OpenTelemetryControllerImpl} by calling {@link OpenTelemetryImpl#close()} and {@link MeterProviderImpl#close()} and waits for it to complete. * The shutdown is final, i.e., once this {@link OpenTelemetryImpl} is shutdown, it cannot be re-enabled! + *

+ * Only use this method for testing or when the JVM is shutting down. */ @Override synchronized public void shutdown() { + if (isStopped()) { + return; + } + if (!isShuttingDown.compareAndSet(false, true)) { + log.info("Multiple shutdown calls"); + } long start = System.nanoTime(); // close tracing @@ -280,13 +271,13 @@ synchronized public void shutdown() { long startMeterProviderShutdown = System.nanoTime(); // note: close calls SdkMeterProvider#shutdown, which calls MetricReader#shutdown, which calls MetricExporter#shutdown CompletableResultCode shutdownResult = meterProvider.close(); - log.info("time to shut down {}: {} ms (success={})", meterProvider, (System.nanoTime() - startMeterProviderShutdown) * 1e-6, shutdownResult.isSuccess()); } GlobalMeterProvider.set(null); GlobalOpenTelemetry.resetForTest(); configured = false; enabled = false; stopped = true; + isShuttingDown.set(false); // set all OTEL related fields to null openTelemetry = null; @@ -296,7 +287,7 @@ synchronized public void shutdown() { spanExporter = null; spanProcessor = null; - log.info("Shut down {}. The shutdown process took {} ms", getClass().getSimpleName(), (System.nanoTime() - start) * 1e-6); + log.info("Shut down {}. The shutdown process took {} ms", getClass().getSimpleName(), (System.nanoTime() - start) / 1000000); } @Override @@ -309,6 +300,56 @@ synchronized public void notifyMetricsSettingsChanged() { metricSettingsChanged = true; } + /** + * Initializes tracing components, i.e., {@link #openTelemetry}, {@link #spanExporter}, {@link #spanProcessor}, {@link #sampler} and {@link SdkTracerProvider} + * + * @param configuration + */ + @VisibleForTesting + void initTracing(InspectitConfig configuration) { + double sampleProbability = configuration.getTracing().getSampleProbability(); + + // set up sampler + sampler = new DynamicSampler(sampleProbability); + + // set up spanProcessor and spanExporter + spanExporter = new DynamicMultiSpanExporter(); + spanProcessor = BatchSpanProcessor.builder(spanExporter) + .setMaxExportBatchSize(configuration.getTracing().getMaxExportBatchSize()) + .setScheduleDelay(configuration.getTracing().getScheduleDelayMillis(), TimeUnit.MILLISECONDS) + .build(); + // build TracerProvider + SdkTracerProviderBuilder builder = SdkTracerProvider.builder() + .setSampler(sampler) + .setResource(serviceNameResource) + .addSpanProcessor(spanProcessor); + + // build the SdkTracerProvider + SdkTracerProvider tracerProvider = builder.build(); + + // build OTEL + OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() + .setTracerProvider(tracerProvider) + .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) + .build(); + + // if the OpenTelemetryImpl has not been build, then build and register it + openTelemetry = OpenTelemetryImpl.builder().openTelemetry(openTelemetrySdk).build(); + + // check if any OpenTelemetry has been registered to GlobalOpenTelemetry. + // If so, reset it. + if (null != OpenTelemetryUtils.getGlobalOpenTelemetry()) { + log.info("reset {}", GlobalOpenTelemetry.get().getClass().getName()); + GlobalOpenTelemetry.resetForTest(); + } + + // set GlobalOpenTelemetry + openTelemetry.registerGlobal(); + + // update the OTEL_TRACER field in OpenTelemetrySpanBuilderImpl in case that it was already set + OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); + } + /** * Configures the tracing, i.e. {@link #openTelemetry} and the related {@link SdkTracerProvider}. A new {@link SdkTracerProvider} is only built once or after {@link #shutdown()} was called. * @@ -318,77 +359,12 @@ synchronized public void notifyMetricsSettingsChanged() { */ @VisibleForTesting synchronized boolean configureTracing(InspectitConfig configuration) { - if (!enabled) { + if (!enabled || stopped) { return true; } try { - - boolean buildTracerProvider = null == openTelemetry; - - // set up the sampler if necessary - double sampleProbability = configuration.getTracing().getSampleProbability(); - if (null == sampler) { - sampler = new DynamicSampler(sampleProbability); - buildTracerProvider = true; - } // update sample probability - sampler.setSampleProbability(sampleProbability); - - // set up span processor and span exporter if necessary - if (null == spanProcessor || spanExporter == null) { - // create new span exporter if null - if (null == spanExporter) { - spanExporter = new DynamicMultiSpanExporter(); - // register the span exporters for all registered services - for (DynamicallyActivatableTraceExporterService service : registeredTraceExportServices.values()) { - spanExporter.registerSpanExporter(service.getName(), service.getSpanExporter()); - } - } - // build the span processor - spanProcessor = BatchSpanProcessor.builder(spanExporter).build(); - buildTracerProvider = true; - } - - // if the TracerProvider needs to be build, build it and register the OpenTelemetryImpl - if (buildTracerProvider) { - // build TracerProvider - SdkTracerProviderBuilder builder = SdkTracerProvider.builder() - .setSampler(sampler) - .setResource(serviceNameResource) - .addSpanProcessor(spanProcessor); - - // build the SdkTracerProvider - SdkTracerProvider tracerProvider = builder.build(); - - // build OTEL - OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() - .setTracerProvider(tracerProvider) - .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) - .build(); - - // if the OpenTelemetryImpl has not been build, then build and register it - if (null == openTelemetry) { - openTelemetry = OpenTelemetryImpl.builder().openTelemetry(openTelemetrySdk).build(); - - // check if any OpenTelemetry has been registered to GlobalOpenTelemetry. - // If so, reset it. - if (null != OpenTelemetryUtils.getGlobalOpenTelemetry()) { - log.info("reset {}", GlobalOpenTelemetry.get().getClass().getName()); - GlobalOpenTelemetry.resetForTest(); - } - - // set GlobalOpenTelemetry - openTelemetry.registerGlobal(); - } - // otherwise, just update the underlying OpenTelemetrySdk - else { - openTelemetry.set(openTelemetrySdk); - } - - // update the OTEL_TRACER field in OpenTelemetrySpanBuilderImpl - OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); - } - + sampler.setSampleProbability(configuration.getTracing().getSampleProbability()); return true; } catch (Exception e) { log.error("Failed to configure OpenTelemetry Tracing", e); @@ -409,16 +385,18 @@ synchronized boolean configureMeterProvider(InspectitConfig configuration) { return true; } try { + + // build new SdkMeterProvider SdkMeterProviderBuilder builder = SdkMeterProvider.builder().setResource(serviceNameResource); + // register metric reader for each service for (DynamicallyActivatableMetricsExporterService metricsExportService : registeredMetricExporterServices.values()) { - log.info("add metricReader for {} ({})", metricsExportService, metricsExportService.getNewMetricReaderFactory()); builder.registerMetricReader(OpenCensusMetrics.attachTo(metricsExportService.getNewMetricReaderFactory())); } SdkMeterProvider sdkMeterProvider = builder.build(); - // if the MeterProvider is null, build and register it + // if the MeterProviderImpl is null, build and register it if (null == meterProvider) { meterProvider = MeterProviderImpl.builder().meterProvider(sdkMeterProvider).build(); meterProvider.registerGlobal(); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java index 79d2457fd3..85617aad96 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java @@ -43,6 +43,8 @@ class OpenTelemetryControllerImplTest { @BeforeEach void initOpenTelemetryController() { + // mock max-export-batch-size to avoid exceptions + when(env.getCurrentConfig().getTracing().getMaxExportBatchSize()).thenReturn(512); openTelemetryController.env = env; openTelemetryController.init(); openTelemetryController.start(); @@ -147,6 +149,44 @@ void testShutdown() { assertThat(OpenTelemetry.noop() == GlobalOpenTelemetry.get()); } + /** + * A noop {@link DynamicallyActivatableTraceExporterService} for testing + */ + class TestTraceExporterService extends DynamicallyActivatableTraceExporterService { + + @Override + public SpanExporter getSpanExporter() { + try { + Method m = Class.forName("io.opentelemetry.sdk.trace.export.NoopSpanExporter") + .getDeclaredMethod("getInstance"); + m.setAccessible(true); + return (SpanExporter) m.invoke(null); + } catch (Throwable t) { + t.printStackTrace(); + return null; + } + } + + @Override + protected boolean checkEnabledForConfig(InspectitConfig configuration) { + return false; + } + + @Override + protected boolean doEnable(InspectitConfig configuration) { + return true; + } + + @Override + protected boolean doDisable() { + return true; + } + + @Override + public String getName() { + return getClass().getSimpleName(); + } + } } @Nested @@ -229,78 +269,39 @@ void testShutdown() { openTelemetryController.shutdown(); assertThat(null == GlobalMeterProvider.get()); } - } - - /** - * A noop {@link DynamicallyActivatableTraceExporterService} for testing - */ - static class TestTraceExporterService extends DynamicallyActivatableTraceExporterService { - - @Override - public SpanExporter getSpanExporter() { - try { - Method m = Class.forName("io.opentelemetry.sdk.trace.export.NoopSpanExporter") - .getDeclaredMethod("getInstance"); - m.setAccessible(true); - return (SpanExporter) m.invoke(null); - } catch (Throwable t) { - t.printStackTrace(); - return null; - } - } - - @Override - protected boolean checkEnabledForConfig(InspectitConfig configuration) { - return false; - } - @Override - protected boolean doEnable(InspectitConfig configuration) { - return true; - } - - @Override - protected boolean doDisable() { - return true; - } - - @Override - public String getName() { - return getClass().getSimpleName(); - } - } - - /** - * A noop {@link DynamicallyActivatableMetricsExporterService} for testing - */ - static class TestMetricsExporterService extends DynamicallyActivatableMetricsExporterService { + /** + * A noop {@link DynamicallyActivatableMetricsExporterService} for testing + */ + class TestMetricsExporterService extends DynamicallyActivatableMetricsExporterService { - @Mock - MetricExporter metricExporter; + @Mock + MetricExporter metricExporter; - @Override - public MetricReaderFactory getNewMetricReaderFactory() { - return PeriodicMetricReader.newMetricReaderFactory(new LoggingMetricExporter()); - } + @Override + public MetricReaderFactory getNewMetricReaderFactory() { + return PeriodicMetricReader.newMetricReaderFactory(new LoggingMetricExporter()); + } - @Override - protected boolean checkEnabledForConfig(InspectitConfig configuration) { - return false; - } + @Override + protected boolean checkEnabledForConfig(InspectitConfig configuration) { + return false; + } - @Override - protected boolean doEnable(InspectitConfig configuration) { - return false; - } + @Override + protected boolean doEnable(InspectitConfig configuration) { + return false; + } - @Override - protected boolean doDisable() { - return false; - } + @Override + protected boolean doDisable() { + return false; + } - @Override - public String getName() { - return "test-metrics-exporter-service"; + @Override + public String getName() { + return "test-metrics-exporter-service"; + } } } From 0fdac4b7c2fa52af7c1a595fa78ee2b66bbcc6fb Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 18 Jan 2022 10:20:51 +0100 Subject: [PATCH 55/86] #1269: Replaced calls to OpenCensusUtils#flushSpanExporter with Instances#openTelemetryController#flush() --- .../ocelot/config/model/tracing/TracingSettings.java | 5 ++++- .../core/opentelemetry/OpenTelemetryControllerImpl.java | 2 +- .../inspectit/ocelot/core/utils/OpenTelemetryUtils.java | 8 ++++++++ .../core/exporter/JaegerExporterServiceIntTest.java | 6 +++--- .../core/exporter/ZipkinExporterServiceIntTest.java | 6 +++--- .../inspectit/ocelot/core/testutils/OpenCensusUtils.java | 8 ++++++-- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java index a884cece87..4a0f77e2e6 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java @@ -7,7 +7,7 @@ import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; -import java.util.concurrent.TimeUnit; +import javax.validation.constraints.Positive; @Data @NoArgsConstructor @@ -65,11 +65,14 @@ public enum AddCommonTags { /** * The maximum batch size for every span export. Default value is 512. */ + @Positive private int maxExportBatchSize = DEFAULT_MAX_EXPORT_BATCH_SIZE; static final long DEFAULT_SCHEDULE_DELAY_MILLIS = 5000; + /** * Delay interval between two consecutive exports in milliseconds. Default value is 5000ms. */ + @Positive private long scheduleDelayMillis = DEFAULT_SCHEDULE_DELAY_MILLIS; } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index 12b86352d7..eda9cf4e11 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -236,7 +236,7 @@ synchronized public boolean start() { } /** - * Flushes the {@link #openTelemetry} and {@link #meterProvider} and waits for it to complete + * Flushes the all pending spans ({@link #openTelemetry}) and metrics ({@link #meterProvider}) and waits for it to complete. */ @Override public void flush() { diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java index 7d07479d9e..cd07bbcf20 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java @@ -6,6 +6,7 @@ import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.trace.SdkTracerProvider; import lombok.extern.slf4j.Slf4j; +import rocks.inspectit.ocelot.bootstrap.Instances; import java.lang.reflect.Field; import java.util.concurrent.CountDownLatch; @@ -114,4 +115,11 @@ public static OpenTelemetry getGlobalOpenTelemetry() { } return openTelemetry; } + + /** + * {@link Instances#openTelemetryController#flush() flushes} all pending spans and metrics waits for it to complete. + */ + public static void flush(){ + Instances.openTelemetryController.flush(); + } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java index c869fb4fde..709730d914 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java @@ -10,8 +10,8 @@ import org.slf4j.LoggerFactory; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; +import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.core.SpringTestBase; -import rocks.inspectit.ocelot.core.testutils.OpenCensusUtils; import java.util.concurrent.TimeUnit; @@ -19,7 +19,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.awaitility.Awaitility.await; -@TestPropertySource(properties = {"inspectit.exporters.tracing.jaeger.url=http://127.0.0.1:14268/api/traces", "inspectit.exporters.tracing.jaeger.grpc=http://127.0.0.1:14267/api/traces","inspectit.tracing.max-export-batch-size=1"}) +@TestPropertySource(properties = {"inspectit.exporters.tracing.jaeger.url=http://127.0.0.1:14268/api/traces", "inspectit.exporters.tracing.jaeger.grpc=http://127.0.0.1:14267/api/traces", "inspectit.tracing.max-export-batch-size=1"}) @DirtiesContext public class JaegerExporterServiceIntTest extends SpringTestBase { @@ -53,7 +53,7 @@ void verifyTraceSent() throws InterruptedException { logger.info("Wait for Jaeger to process the span..."); Thread.sleep(1100L); - OpenCensusUtils.flushSpanExporter(); + Instances.openTelemetryController.flush(); await().atMost(15, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { verify(postRequestedFor(urlPathEqualTo(JAEGER_PATH))); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java index d4109a9d49..4480eda91b 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java @@ -10,8 +10,8 @@ import org.slf4j.LoggerFactory; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; +import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.core.SpringTestBase; -import rocks.inspectit.ocelot.core.testutils.OpenCensusUtils; import java.util.concurrent.TimeUnit; @@ -19,7 +19,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.awaitility.Awaitility.await; -@TestPropertySource(properties = {"inspectit.exporters.tracing.zipkin.url=http://127.0.0.1:9411/api/v2/spans", "inspectit.tracing.max-export-batch-size=1"}) +@TestPropertySource(properties = {"inspectit.exporters.tracing.zipkin.url=http://127.0.0.1:9411/api/v2/spans", "inspectit.tracing.max-export-batch-size=512", "inspectit.tracing.schedule-delay-millis=20000"}) @DirtiesContext public class ZipkinExporterServiceIntTest extends SpringTestBase { @@ -53,7 +53,7 @@ void verifyTraceSent() throws InterruptedException { logger.info("Wait for Jaeger to process the span..."); Thread.sleep(1100L); - OpenCensusUtils.flushSpanExporter(); + Instances.openTelemetryController.flush(); await().atMost(15, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { verify(postRequestedFor(urlPathEqualTo(ZIPKIN_PATH)).withRequestBody(containing("zipkinspan"))); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/testutils/OpenCensusUtils.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/testutils/OpenCensusUtils.java index e1c4364450..3b881cf157 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/testutils/OpenCensusUtils.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/testutils/OpenCensusUtils.java @@ -4,6 +4,7 @@ import io.opencensus.trace.export.SpanExporter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; import java.lang.reflect.Method; @@ -13,10 +14,13 @@ public class OpenCensusUtils { /** * Flushes the span exporters. + * + * This method is no longer supported. Please us {@link OpenTelemetryUtils#flush()} instead. */ - public static void flushSpanExporter() { + @Deprecated + public static void flushSpanExporter() { try { - SpanExporter exporter = Tracing.getExportComponent().getSpanExporter(); + SpanExporter exporter = Tracing.getExportComponent().getSpanExporter(); Method flushMethod = exporter.getClass().getDeclaredMethod("flush"); flushMethod.setAccessible(true); flushMethod.invoke(exporter); From 8ff8bd687459fae3c9094ed78d0793ae7cdc8078 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 18 Jan 2022 10:21:52 +0100 Subject: [PATCH 56/86] #1269: Renamed OtlpTraceExporterService to OtlpGrpcTraceExporterService and adjusted related classes and yml --- ...gs.java => OtlpGrpcTraceExporterSettings.java} | 2 +- .../exporters/trace/TraceExportersSettings.java | 2 +- .../inspectit/ocelot/config/default/exporters.yml | 4 ++-- ...ice.java => OtlpGrpcTraceExporterService.java} | 15 +++++++++------ 4 files changed, 13 insertions(+), 10 deletions(-) rename inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/{OtlpTraceExporterSettings.java => OtlpGrpcTraceExporterSettings.java} (89%) rename inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/{OtlpTraceExporterService.java => OtlpGrpcTraceExporterService.java} (77%) diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpGrpcTraceExporterSettings.java similarity index 89% rename from inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java rename to inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpGrpcTraceExporterSettings.java index 6ab0589241..358e1ba4dd 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpGrpcTraceExporterSettings.java @@ -8,7 +8,7 @@ */ @Data @NoArgsConstructor -public class OtlpTraceExporterSettings { +public class OtlpGrpcTraceExporterSettings { private boolean enabled; diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java index b87e1eff57..85343a0638 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java @@ -19,5 +19,5 @@ public class TraceExportersSettings { private LoggingTraceExporterSettings logging; @Valid - private OtlpTraceExporterSettings otlp; + private OtlpGrpcTraceExporterSettings otlpGrpc; } diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml index 27c91a82c8..d8baead956 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml @@ -64,7 +64,7 @@ inspectit: logging: enabled: false - # settings for the OtlpGrpcSpanExporter used in OtlpTraceExporterService - otlp: + # settings for the OtlpGrpcSpanExporter used in OtlpGrpcTraceExporterService + otlp-grpc: enabled: true url: null diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java similarity index 77% rename from inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java rename to inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java index cfa949dc07..06a2d9df39 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java @@ -7,7 +7,7 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; -import rocks.inspectit.ocelot.config.model.exporters.trace.OtlpTraceExporterSettings; +import rocks.inspectit.ocelot.config.model.exporters.trace.OtlpGrpcTraceExporterSettings; import javax.validation.Valid; @@ -17,25 +17,25 @@ */ @Component @Slf4j -public class OtlpTraceExporterService extends DynamicallyActivatableTraceExporterService { +public class OtlpGrpcTraceExporterService extends DynamicallyActivatableTraceExporterService { @Getter private SpanExporter spanExporter; - public OtlpTraceExporterService() { - super("exporters.tracing.otlp", "tracing.enabled"); + public OtlpGrpcTraceExporterService() { + super("exporters.tracing.otlp-grpc", "tracing.enabled"); } @Override protected boolean checkEnabledForConfig(InspectitConfig configuration) { - @Valid OtlpTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlp(); + @Valid OtlpGrpcTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlpGrpc(); return configuration.getTracing().isEnabled() && !StringUtils.isEmpty(otlp.getUrl()) && otlp.isEnabled(); } @Override protected boolean doEnable(InspectitConfig configuration) { try { - OtlpTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlp(); + OtlpGrpcTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlpGrpc(); log.info("Starting OTLP Trace Exporter with endpoint {}", otlp.getUrl()); // create span exporter @@ -46,6 +46,9 @@ protected boolean doEnable(InspectitConfig configuration) { return true; } catch (Throwable t) { log.error("Error creating OTLP trace exporter", t); + if (true) { + throw t; + } return false; } } From 0a010bc0664fc3049a1f77f95eaf3557f7587643 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 18 Jan 2022 10:54:53 +0100 Subject: [PATCH 57/86] #1269: Fixed OtlpGrpcTraceExporterService to use "otlGrpc" instead of "otlp-grpc", which led to exceptions --- .../ocelot/core/exporter/OtlpGrpcTraceExporterService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java index 06a2d9df39..644572e9bd 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java @@ -13,7 +13,7 @@ /** * Service for {@link io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter}. - * Can be dynamically started and stopped using the exporters.trace.otlp.enabled configuration + * Can be dynamically started and stopped using the exporters.trace.otlp-grpc.enabled configuration */ @Component @Slf4j @@ -23,7 +23,7 @@ public class OtlpGrpcTraceExporterService extends DynamicallyActivatableTraceExp private SpanExporter spanExporter; public OtlpGrpcTraceExporterService() { - super("exporters.tracing.otlp-grpc", "tracing.enabled"); + super("exporters.tracing.otlpGrpc", "tracing.enabled"); } @Override From a099027afe37a5be3bdf137157636dfbb29ae3bf Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 25 Jan 2022 18:24:19 +0100 Subject: [PATCH 58/86] #1279: Upgraded OTEL to v1.10.0, refactored OpenTelemetryControllerImpl and OpenTelemetryImpl. MeterProviderImpl was removed as the global MeterProvider was moved to OpenTelemetry in OTEL v1.10.0 --- gradle.properties | 4 +- inspectit-ocelot-core/build.gradle | 2 +- .../exporter/LoggingTraceExporterService.java | 2 +- .../DynamicMultiSpanExporter.java | 8 + .../core/opentelemetry/MeterProviderImpl.java | 113 ------------ .../OpenTelemetryControllerImpl.java | 165 ++++++++++-------- .../core/opentelemetry/OpenTelemetryImpl.java | 66 ++++++- .../ocelot/core/utils/OpenTelemetryUtils.java | 2 - .../LoggingMetricsExporterServiceIntTest.java | 15 +- .../PrometheusExporterServiceIntTest.java | 32 +++- .../OpenTelemetryControllerImplTest.java | 122 +++++++++++-- 11 files changed, 310 insertions(+), 221 deletions(-) delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java diff --git a/gradle.properties b/gradle.properties index 2a3950a6f8..6ae4e690d0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ org.gradle.jvmargs=-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/circleci/inspectit/repo/heapdump.bin # Ensure to adapt the netty version (inspectit-ocelot-core/build.gradle) when changing the OpenCensus version openCensusVersion=0.28.3 -openTelemetryVersion=1.9.1 -openTelemetryAlphaVersion=1.9.1-alpha +openTelemetryVersion=1.10.0 +openTelemetryAlphaVersion=1.10.0-alpha springVersion=2.1.1.RELEASE prometheusClientVersion=0.6.0 # appropriate netty version, see https://github.com/census-instrumentation/opencensus-java/blob/master/exporters/trace/ocagent/README.md diff --git a/inspectit-ocelot-core/build.gradle b/inspectit-ocelot-core/build.gradle index 27f226ea05..3ddd966985 100644 --- a/inspectit-ocelot-core/build.gradle +++ b/inspectit-ocelot-core/build.gradle @@ -29,7 +29,6 @@ dependencies { platform("io.opentelemetry:opentelemetry-bom:${openTelemetryVersion}"), platform("io.opentelemetry:opentelemetry-bom-alpha:${openTelemetryAlphaVersion}"), 'io.opentelemetry:opentelemetry-api', - 'io.opentelemetry:opentelemetry-api-metrics', "io.opentelemetry:opentelemetry-sdk", "io.opentelemetry:opentelemetry-sdk-metrics", "io.opentelemetry:opentelemetry-semconv", @@ -107,6 +106,7 @@ dependencies { platform("io.opentelemetry:opentelemetry-bom:${openTelemetryVersion}"), platform("io.opentelemetry:opentelemetry-bom-alpha:${openTelemetryAlphaVersion}"), "io.opentelemetry:opentelemetry-sdk", + "io.opentelemetry:opentelemetry-sdk-metrics", "io.opentelemetry:opentelemetry-opencensus-shim", "io.opentelemetry:opentelemetry-semconv", "io.opentelemetry:opentelemetry-sdk-testing", diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java index 8510fdca54..a1084ccf4d 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java @@ -31,7 +31,7 @@ protected void init() { super.init(); // create span exporter and span processors - spanExporter = new LoggingSpanExporter(); + spanExporter = LoggingSpanExporter.create(); } @Override diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/DynamicMultiSpanExporter.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/DynamicMultiSpanExporter.java index 8e7191f39c..de23efab27 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/DynamicMultiSpanExporter.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/DynamicMultiSpanExporter.java @@ -21,6 +21,14 @@ @Slf4j public class DynamicMultiSpanExporter implements SpanExporter { + /** + * Returns a new {@link DynamicMultiSpanExporter} + * @return + */ + public static DynamicMultiSpanExporter create(){ + return new DynamicMultiSpanExporter(); + } + /** * The {@link SpanExporter}s that all received spans are forwarded to. */ diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java deleted file mode 100644 index f0b5f095ba..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/MeterProviderImpl.java +++ /dev/null @@ -1,113 +0,0 @@ -package rocks.inspectit.ocelot.core.opentelemetry; - -import io.opentelemetry.api.metrics.GlobalMeterProvider; -import io.opentelemetry.api.metrics.MeterBuilder; -import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import lombok.Builder; -import lombok.extern.slf4j.Slf4j; -import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Custom implementation of {@link MeterProvider} that wraps {@link SdkMeterProvider}. - * This wrapper is used to change {@link #meterProvider} without resetting {@link io.opentelemetry.api.metrics.GlobalMeterProvider#set(MeterProvider)}. - * For use, create the {@link MeterProviderImpl} and set it once to {@link io.opentelemetry.api.metrics.GlobalMeterProvider#set(MeterProvider)}. If the {@link SdkMeterProvider} changes, simply call {@link #set(SdkMeterProvider)}. If the {@link #meterProvider} was previously set, it will be flushed and closed and blocks waiting for it. - */ -@Slf4j -@Builder -public class MeterProviderImpl implements MeterProvider { - - /** - * The {@link SdkMeterProvider} implementation - */ - private SdkMeterProvider meterProvider; - - @Builder.Default - private Object lock = new Object(); - - /** - * Registers the {@link SdkMeterProvider}. - * If an instance of {@link SdkMeterProvider} was already registered, it is {@link SdkMeterProvider#forceFlush() flushed} and {@link SdkMeterProvider#shutdown() shutdown} and blocks waiting for it to complete before the new {@link SdkMeterProvider} is set to {@link #meterProvider} - * - * @param meterProvider - */ - public void set(SdkMeterProvider meterProvider) { - synchronized (lock) { - // shut down previous meterProvider if set - if (null != this.meterProvider && this.meterProvider != meterProvider) { - OpenTelemetryUtils.stopMeterProvider(this.meterProvider, true); - } - // set SdkMeterProvider - this.meterProvider = meterProvider; - } - } - - /** - * Gets the currently registered {@link SdkMeterProvider} - * - * @return The currently registered {@link SdkMeterProvider} - */ - public SdkMeterProvider get() { - return meterProvider; - } - - /** - * Shuts down the currently registered {@link #meterProvider} and blocks waiting for it to complete. - * - * @return - */ - public CompletableResultCode close() { - CompletableResultCode result = new CompletableResultCode(); - if (null != meterProvider) { - synchronized (lock) { - result = OpenTelemetryUtils.stopMeterProvider(meterProvider, true); - } - } else { - result.succeed(); - } - return result; - } - - /** - * Calls {@link SdkMeterProvider#forceFlush() meterProvider.forceFlush} - * - * @return The resulting {@link CompletableResultCode} completes when all complete. - */ - public CompletableResultCode forceFlush() { - return meterProvider != null ? meterProvider.forceFlush() : CompletableResultCode.ofSuccess(); - } - - /** - * {@link SdkMeterProvider#forceFlush() flushes} the {@link #meterProvider} and waits for it to complete. - */ - public void flush() { - CompletableResultCode resultCode = forceFlush(); - if (!resultCode.isDone()) { - CountDownLatch latch = new CountDownLatch(1); - resultCode.whenComplete(() -> latch.countDown()); - try { - latch.await(15, TimeUnit.SECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - /** - * Registers {@link MeterProvider this} to {@link GlobalMeterProvider#set(MeterProvider)} - */ - public MeterProvider registerGlobal() { - GlobalMeterProvider.set(this); - return this; - } - - @Override - public MeterBuilder meterBuilder(String instrumentationName) { - return meterProvider.meterBuilder(instrumentationName); - } - -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index eda9cf4e11..30f8826f8a 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -4,8 +4,6 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.GlobalMeterProvider; -import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; @@ -13,7 +11,6 @@ import io.opentelemetry.extension.trace.propagation.JaegerPropagator; import io.opentelemetry.opencensusshim.metrics.OpenCensusMetrics; import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.resources.Resource; @@ -48,7 +45,7 @@ import java.util.concurrent.atomic.AtomicBoolean; /** - * The implementation of {@link IOpenTelemetryController}. The {@link OpenTelemetryControllerImpl} configures {@link GlobalOpenTelemetry} (tracing) and {@link GlobalMeterProvider} (metrics). + * The implementation of {@link IOpenTelemetryController}. The {@link OpenTelemetryControllerImpl} configures {@link GlobalOpenTelemetry}. * The individual {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService services}, i.e., {@link DynamicallyActivatableMetricsExporterService} and {@link DynamicallyActivatableTraceExporterService}, register to and unregister from {@link OpenTelemetryControllerImpl this}. * Important note: {@link #shutdown() shutting down} the {@link OpenTelemetryControllerImpl} is final and cannot be revoked. */ @@ -74,7 +71,7 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { private boolean metricSettingsChanged = false; /** - * whether {@link GlobalOpenTelemetry} and {@link GlobalMeterProvider} have successfully been configured. + * whether {@link GlobalOpenTelemetry} has been successfully been configured. */ @Getter // TODO: make sure that configured is set accordingly when OTEL is being reconfigured (false while configuring, true after success) @@ -114,11 +111,18 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { private OpenTelemetryImpl openTelemetry; /** - * The {@link MeterProviderImpl} that wraps {@link SdkMeterProvider} + * The currently active {@link SdkMeterProvider} */ @VisibleForTesting @Getter(AccessLevel.PACKAGE) - private MeterProviderImpl meterProvider; + private SdkMeterProvider meterProvider; + + /** + * The currently active {@link SdkTracerProvider} + */ + @VisibleForTesting + @Getter(AccessLevel.PACKAGE) + private SdkTracerProvider tracerProvider; @Autowired InspectitEnvironment env; @@ -143,11 +147,7 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { @PostConstruct @VisibleForTesting void init() { - // create the service name resource - serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, env.getCurrentConfig() - .getServiceName())); - - initTracing(env.getCurrentConfig()); + initOtel(env.getCurrentConfig()); // close the tracer provider when the JVM is shutting down Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); @@ -176,7 +176,7 @@ public boolean isConfiguring() { /** * Configures and registers {@link io.opentelemetry.api.OpenTelemetry}, triggered by the {@link rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent} triggered * For tracing, the {@link SdkTracerProvider} is reconfigured and updated in the {@link GlobalOpenTelemetry}. - * For metrics, the {@link SdkMeterProvider} is reconfigured and updated in the {@link GlobalMeterProvider} + * For metrics, the {@link SdkMeterProvider} is reconfigured and updated in the {@link GlobalOpenTelemetry} * * @return */ @@ -188,7 +188,7 @@ synchronized boolean configureOpenTelemetry() { if (stopped) { return false; } - boolean success = false; + boolean success = true; if (!isConfiguring.compareAndSet(false, true)) { log.info("Multiple configure calls"); return true; @@ -205,13 +205,30 @@ synchronized boolean configureOpenTelemetry() { .getSampleProbability()) { tracingSettingsChanged = true; } - // configure tracing if not configured or when tracing settings changed - boolean successConfigureTracing = !(tracingSettingsChanged || !configured) ? true : configureTracing(configuration); - // configure meter provider (metrics) if not configured or when metrics settings changed - boolean successConfigureMeterProvider = !(metricSettingsChanged || !configured) ? true : configureMeterProvider(configuration); + if (!configured || metricSettingsChanged || tracingSettingsChanged) { - success = successConfigureTracing && successConfigureMeterProvider; + // configure tracing if not configured or when tracing settings changed + SdkTracerProvider sdkTracerProvider = !(tracingSettingsChanged || !configured) ? tracerProvider : configureTracing(configuration); + + // configure meter provider (metrics) if not configured or when metrics settings changed + SdkMeterProvider sdkMeterProvider = !(metricSettingsChanged || !configured) ? meterProvider : configureMeterProvider(configuration); + + // only if metrics settings changed or OTEL has not been configured before, we need to rebuild the OpenTelemetrySdk + if (metricSettingsChanged || !configured) { + OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() + .setTracerProvider(sdkTracerProvider) + .setMeterProvider(sdkMeterProvider) + .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) + .build(); + // update OTEL + openTelemetry.set(openTelemetrySdk, false, false); + } + success = null != sdkMeterProvider && null != sdkTracerProvider; + // update meterProvider and tracerProvider + meterProvider = sdkMeterProvider; + tracerProvider = sdkTracerProvider; + } if (success) { log.info("Successfully configured OpenTelemetry with tracing and metrics"); @@ -241,7 +258,6 @@ synchronized public boolean start() { @Override public void flush() { openTelemetry.flush(); - meterProvider.flush(); } /** @@ -260,19 +276,14 @@ synchronized public void shutdown() { } long start = System.nanoTime(); - // close tracing + // close OTEL if (null != openTelemetry) { // note: close calls SdkTracerProvider#shutdown, which calls SpanProcessor#shutdown, which calls SpanExporter#shutdown. // thus, the spanProcessor and spanExporter are shut down in this process and cannot be used later + // note: also close calls SdkMeterProvider#shutdown, which calls MetricReader#shutdown, which calls MetricExporter#shutdown openTelemetry.close(); } - // close metric provider - if (null != meterProvider) { - long startMeterProviderShutdown = System.nanoTime(); - // note: close calls SdkMeterProvider#shutdown, which calls MetricReader#shutdown, which calls MetricExporter#shutdown - CompletableResultCode shutdownResult = meterProvider.close(); - } - GlobalMeterProvider.set(null); + GlobalOpenTelemetry.resetForTest(); configured = false; enabled = false; @@ -301,35 +312,27 @@ synchronized public void notifyMetricsSettingsChanged() { } /** - * Initializes tracing components, i.e., {@link #openTelemetry}, {@link #spanExporter}, {@link #spanProcessor}, {@link #sampler} and {@link SdkTracerProvider} + * Initializes tracer and meter provider components, i.e., {@link #openTelemetry}, {@link #spanExporter}, {@link #spanProcessor}, {@link #sampler}, {@link SdkTracerProvider}, and {@link SdkMeterProvider} * * @param configuration */ @VisibleForTesting - void initTracing(InspectitConfig configuration) { - double sampleProbability = configuration.getTracing().getSampleProbability(); + void initOtel(InspectitConfig configuration) { - // set up sampler - sampler = new DynamicSampler(sampleProbability); + // create the service name resource + serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, env.getCurrentConfig() + .getServiceName())); - // set up spanProcessor and spanExporter - spanExporter = new DynamicMultiSpanExporter(); - spanProcessor = BatchSpanProcessor.builder(spanExporter) - .setMaxExportBatchSize(configuration.getTracing().getMaxExportBatchSize()) - .setScheduleDelay(configuration.getTracing().getScheduleDelayMillis(), TimeUnit.MILLISECONDS) - .build(); - // build TracerProvider - SdkTracerProviderBuilder builder = SdkTracerProvider.builder() - .setSampler(sampler) - .setResource(serviceNameResource) - .addSpanProcessor(spanProcessor); + // build new SdkTracerProvider + tracerProvider = buildSdkTracerProvider(configuration); - // build the SdkTracerProvider - SdkTracerProvider tracerProvider = builder.build(); + // build new SdkMeterProvider + meterProvider = SdkMeterProvider.builder().setResource(serviceNameResource).build(); // build OTEL OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) + .setMeterProvider(meterProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) .build(); @@ -355,37 +358,72 @@ void initTracing(InspectitConfig configuration) { * * @param configuration * - * @return Whether {@link #openTelemetry} was successfully configured + * @return The updated {@link SdkTracerProvider} or null if the configuration failed. */ @VisibleForTesting - synchronized boolean configureTracing(InspectitConfig configuration) { + synchronized SdkTracerProvider configureTracing(InspectitConfig configuration) { if (!enabled || stopped) { - return true; + return null; } try { // update sample probability sampler.setSampleProbability(configuration.getTracing().getSampleProbability()); - return true; + return tracerProvider; + } catch (Exception e) { log.error("Failed to configure OpenTelemetry Tracing", e); - return false; + return null; } } /** - * Configures the {@link SdkMeterProvider} and registers it as the {@link GlobalMeterProvider} via {@link GlobalMeterProvider#set(MeterProvider)} + * Builds a new {@link SdkTracerProvider} based on the given {@link InspectitConfig} * * @param configuration * - * @return + * @return A new {@link SdkTracerProvider} + */ + private synchronized SdkTracerProvider buildSdkTracerProvider(InspectitConfig configuration) { + double sampleProbability = configuration.getTracing().getSampleProbability(); + + // set up sampler + sampler = new DynamicSampler(sampleProbability); + + // set up spanProcessor and spanExporter + spanExporter = DynamicMultiSpanExporter.create(); + spanProcessor = BatchSpanProcessor.builder(spanExporter) + .setMaxExportBatchSize(configuration.getTracing().getMaxExportBatchSize()) + .setScheduleDelay(configuration.getTracing().getScheduleDelayMillis(), TimeUnit.MILLISECONDS) + .build(); + // build TracerProvider + SdkTracerProviderBuilder builder = SdkTracerProvider.builder() + .setSampler(sampler) + .setResource(serviceNameResource) + .addSpanProcessor(spanProcessor); + + return builder.build(); + } + + /** + * Configures the {@link SdkMeterProvider} + * + * @param configuration + * + * @return The updated {@link SdkMeterProvider} or null if the configuration failed. */ @VisibleForTesting - synchronized boolean configureMeterProvider(InspectitConfig configuration) { - if (!enabled) { - return true; + synchronized SdkMeterProvider configureMeterProvider(InspectitConfig configuration) { + if (!enabled || stopped) { + return null; } try { + // stop the previously registered MeterProvider + if (null != meterProvider) { + long start = System.nanoTime(); + OpenTelemetryUtils.stopMeterProvider(meterProvider, true); + log.info("time to stopMeterProvider: {} ms", (System.nanoTime() - start) / 1000000); + } // build new SdkMeterProvider SdkMeterProviderBuilder builder = SdkMeterProvider.builder().setResource(serviceNameResource); @@ -396,21 +434,11 @@ synchronized boolean configureMeterProvider(InspectitConfig configuration) { SdkMeterProvider sdkMeterProvider = builder.build(); - // if the MeterProviderImpl is null, build and register it - if (null == meterProvider) { - meterProvider = MeterProviderImpl.builder().meterProvider(sdkMeterProvider).build(); - meterProvider.registerGlobal(); - } - // otherwise, just update the internally used SdkMeterProvider - else { - meterProvider.set(sdkMeterProvider); - } - - return true; + return sdkMeterProvider; } catch (Exception e) { log.error("Failed to configure MeterProvider", e); - return false; + return null; } } @@ -537,5 +565,4 @@ private boolean unregisterMetricExporterService(String serviceName) { public boolean unregisterMetricExporterService(DynamicallyActivatableMetricsExporterService service) { return unregisterMetricExporterService(service.getName()); } - } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java index ebdfb4d70f..ec3dbc880b 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java @@ -2,14 +2,18 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.trace.SdkTracerProvider; import lombok.Builder; import lombok.extern.slf4j.Slf4j; import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; +import java.util.Arrays; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -48,16 +52,56 @@ public ContextPropagators getPropagators() { return get().getPropagators(); } + @Override + public MeterProvider getMeterProvider() { + return get().getMeterProvider(); + } + + /** + * Gets the {@link SdkMeterProvider} of the currently registered {@link #openTelemetry}} + * + * @return + */ + private SdkMeterProvider getSdkMeterProvider() { + return openTelemetry.getSdkMeterProvider(); + } + + /** + * Gets the {@link SdkTracerProvider} of the currently registered {@link #openTelemetry}} + * + * @return + */ + private SdkTracerProvider getSdkTracerProvider() { + return openTelemetry.getSdkTracerProvider(); + } + /** - * Registers the {@link OpenTelemetrySdk}. If an {@link OpenTelemetrySdk} was already registered as {@link #openTelemetry}, flush and close it before registering the new {@link OpenTelemetrySdk}. + * Registers the {@link OpenTelemetrySdk}. If an {@link OpenTelemetrySdk} was already registered as {@link #openTelemetry}, flushes and closes the {@link SdkTracerProvider} and {@link SdkMeterProvider} of the previously registered {@link OpenTelemetrySdk} before registering the new {@link OpenTelemetrySdk}. * - * @param openTelemetry + * @param openTelemetry The new {@link OpenTelemetrySdk} to register */ public void set(OpenTelemetrySdk openTelemetry) { + set(openTelemetry, true, true); + } + + /** + * Registers the {@link OpenTelemetry}. If an {@link OpenTelemetrySdk} was already registered as {@link #openTelemetry}, flush and close the {@link SdkTracerProvider} and {@link SdkMeterProvider} of the previously registered {@link OpenTelemetrySdk} if required before registering the new {@link OpenTelemetrySdk} + * + * @param openTelemetry The new {@link OpenTelemetrySdk} to register. + * @param stopPreviousTracerProvider Whether the {@link SdkTracerProvider} of a previously registered {@link OpenTelemetrySdk} shall be closed before registering the new {@link OpenTelemetrySdk} + * @param stopPreviousMeterProvider Whether the {@link SdkMeterProvider} of a previously registered {@link OpenTelemetrySdk} shall be closed before registering the new {@link OpenTelemetrySdk} + */ + public void set(OpenTelemetrySdk openTelemetry, boolean stopPreviousTracerProvider, boolean stopPreviousMeterProvider) { synchronized (lock) { if (null != openTelemetry) { - // stop previous SdkTracerProvider - OpenTelemetryUtils.stopTracerProvider(this.openTelemetry.getSdkTracerProvider()); + // stop previous SdkTracerProvider if settings changed + if (stopPreviousTracerProvider) { + OpenTelemetryUtils.stopTracerProvider(getSdkTracerProvider()); + } + // stop previous SdkMeterProvider if settings changed + if (stopPreviousMeterProvider) { + OpenTelemetryUtils.stopMeterProvider(getSdkMeterProvider()); + } } this.openTelemetry = openTelemetry; } @@ -69,18 +113,22 @@ public void set(OpenTelemetrySdk openTelemetry) { * @return The {@link CompletableResultCode} */ public synchronized CompletableResultCode close() { - CompletableResultCode result; + CompletableResultCode closeTracerProviderResultCode; + CompletableResultCode closeMeterProviderResultCode; synchronized (lock) { - result = OpenTelemetryUtils.stopTracerProvider(openTelemetry.getSdkTracerProvider(), true); + closeTracerProviderResultCode = OpenTelemetryUtils.stopTracerProvider(getSdkTracerProvider(), true); + closeMeterProviderResultCode = OpenTelemetryUtils.stopMeterProvider(getSdkMeterProvider(), true); } - return result; + return CompletableResultCode.ofAll(Arrays.asList(closeMeterProviderResultCode, closeTracerProviderResultCode)); } /** - * {@link io.opentelemetry.sdk.trace.SdkTracerProvider#forceFlush() flushes} the {@link #openTelemetry} and waits for it to complete. + * Flushes the {@link SdkMeterProvider} and {@link SdkTracerProvider} and waits for it to complete. */ public void flush() { - CompletableResultCode resultCode = openTelemetry.getSdkTracerProvider().forceFlush(); + CompletableResultCode flushTracerProvider = null != getSdkTracerProvider() ? getSdkTracerProvider().forceFlush() : CompletableResultCode.ofSuccess(); + CompletableResultCode flushMeterProvider = null != getMeterProvider() ? getSdkMeterProvider().forceFlush() : CompletableResultCode.ofSuccess(); + CompletableResultCode resultCode = CompletableResultCode.ofAll(Arrays.asList(flushTracerProvider, flushMeterProvider)); if (!resultCode.isDone()) { CountDownLatch latch = new CountDownLatch(1); resultCode.whenComplete(() -> latch.countDown()); diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java index cd07bbcf20..136cae8ec7 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenTelemetryUtils.java @@ -39,10 +39,8 @@ public static CompletableResultCode stopMeterProvider(SdkMeterProvider meterProv */ public static CompletableResultCode stopMeterProvider(SdkMeterProvider meterProvider, boolean forceFlush) { // force flush if applicable - long start = System.nanoTime(); if (forceFlush) { // wait until force flush has succeeded - long startFlush = System.nanoTime(); CompletableResultCode flushResult = meterProvider.forceFlush(); if (!flushResult.isDone()) { CountDownLatch latch = new CountDownLatch(1); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java index 430020ec6a..ec389fcab9 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java @@ -2,10 +2,9 @@ import io.github.netmikey.logunit.api.LogCapturer; import io.opencensus.stats.*; +import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.BoundLongCounter; -import io.opentelemetry.api.metrics.GlobalMeterProvider; import io.opentelemetry.api.metrics.LongCounter; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.exporter.logging.LoggingMetricExporter; @@ -17,6 +16,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; +import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.core.SpringTestBase; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; @@ -39,6 +39,10 @@ public class LoggingMetricsExporterServiceIntTest extends SpringTestBase { @BeforeEach void enableService() { localSwitch(true); + Awaitility.await() + .atMost(15, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(Instances.openTelemetryController.isConfigured()).isTrue()); } @AfterEach @@ -86,7 +90,7 @@ void verifyOpenTelemetryMetricsWritten() { assertThat(service.isEnabled()).isTrue(); // get the meter and create a counter - Meter meter = GlobalMeterProvider.get() + Meter meter = GlobalOpenTelemetry.getMeterProvider() .meterBuilder("rocks.inspectit.ocelot") .setInstrumentationVersion("0.0.1") .build(); @@ -94,10 +98,11 @@ void verifyOpenTelemetryMetricsWritten() { .setDescription("Processed jobs") .setUnit("1") .build(); - BoundLongCounter workCounter = counter.bind(Attributes.of(AttributeKey.stringKey("Key"), "SomeWork")); // record counter - workCounter.add(1); + counter.add(1, Attributes.of(AttributeKey.stringKey("Key"), "SomeWork")); + + Instances.openTelemetryController.flush(); // verify that the metric has been exported to the log Awaitility.waitAtMost(15, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java index 650f1aafb2..a96fbe8f9e 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterServiceIntTest.java @@ -1,7 +1,10 @@ package rocks.inspectit.ocelot.core.exporter; import ch.qos.logback.classic.Level; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.metrics.MeterProvider; import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; @@ -16,8 +19,7 @@ import java.io.IOException; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; +import static org.assertj.core.api.Assertions.*; @TestPropertySource(properties = {"inspecit.exporters.metrics.prometheus.enabled=true"}) @DirtiesContext @@ -47,12 +49,13 @@ static void closeClient() throws Exception { } void assertGet200(String url) throws Exception { - int statusCode = testClient.execute(new HttpGet(url)).getStatusLine().getStatusCode(); - + CloseableHttpResponse response = testClient.execute(new HttpGet(url)); + int statusCode = response.getStatusLine().getStatusCode(); assertThat(statusCode).isEqualTo(200); + response.close(); } - void assertUnavailable(String url) throws Exception { + void assertUnavailable(String url) { Throwable throwable = catchThrowable(() -> testClient.execute(new HttpGet(url)) .getStatusLine() .getStatusCode()); @@ -62,6 +65,7 @@ void assertUnavailable(String url) throws Exception { /** * Sets the switch of 'inspectit.exporters.metrics.prometheus.enabled' to the given value + * * @param enabled */ void switchPrometheusExporterService(boolean enabled) { @@ -97,7 +101,7 @@ void testMasterSwitch() throws Exception { @DirtiesContext @Test - void testLocalSwitch() throws Exception { + void testLocalSwitch() { updateProperties(props -> { props.setProperty("inspectit.exporters.metrics.prometheus.enabled", "false"); }); @@ -117,4 +121,20 @@ void testChangePort() throws Exception { assertNoLogsOfLevelOrGreater(Level.WARN); } + @DirtiesContext + @Test + void testRestartPrometheus() throws Exception { + + assertGet200("http://localhost:8888/metrics"); + assertUnavailable("http://localhost:8889/metrics"); + // disable prometheus + updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.prometheus.enabled", "false"); + }); + assertUnavailable("http://localhost:8888/metrics"); + // enable prometheus + enableService(); + assertThat(service.isEnabled()).isTrue(); + assertGet200("http://localhost:8888/metrics"); + } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java index 85617aad96..cc1e1214da 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java @@ -2,31 +2,41 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.metrics.GlobalMeterProvider; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.export.SpanExporter; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.core.SpringTestBase; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableTraceExporterService; +import java.io.IOException; import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.Mockito.*; /** @@ -130,7 +140,8 @@ void testUnregisterTraceExporterService() { @Test void testConfigureTracing() { - TracerProvider tracerProvider = GlobalOpenTelemetry.getTracerProvider(); + TracerProvider globalTracerProvider = GlobalOpenTelemetry.getTracerProvider(); + MeterProvider globalMeterProvider = GlobalOpenTelemetry.getMeterProvider(); // register test service registerTestTraceExporterServiceAndVerify(true); // configure OTEL @@ -140,7 +151,8 @@ void testConfigureTracing() { verify(openTelemetryController, times(0)).configureMeterProvider(any(InspectitConfig.class)); // verify that the tracer provider does not change after tracing has been (re-)configured - assertThat(tracerProvider).isEqualTo(tracerProvider); + assertThat(globalTracerProvider).isSameAs(GlobalOpenTelemetry.getTracerProvider()); + assertThat(globalMeterProvider).isSameAs(GlobalOpenTelemetry.getMeterProvider()); } @Test @@ -246,9 +258,12 @@ void testUnregisterMetricsExporterService() { @Test void testConfigureMetrics() { - MeterProviderImpl meterProviderImpl = openTelemetryController.getMeterProvider(); + MeterProvider globalMeterProvider = GlobalOpenTelemetry.getMeterProvider(); + TracerProvider globalTracerProvider = GlobalOpenTelemetry.getTracerProvider(); + + SdkMeterProvider meterProvider = openTelemetryController.getMeterProvider(); + SdkTracerProvider tracerProvider = openTelemetryController.getTracerProvider(); - MeterProvider globalMeterProvider = GlobalMeterProvider.get(); // register some service registerTestMetricExporterServiceAndVerify(true); // configure OTEL @@ -258,16 +273,18 @@ void testConfigureMetrics() { verify(openTelemetryController, times(1)).configureMeterProvider(any(InspectitConfig.class)); verify(openTelemetryController, times(0)).configureTracing(any(InspectitConfig.class)); - // (global) meter provider should not have changed during configuration - assertThat(openTelemetryController.getMeterProvider()).isSameAs(meterProviderImpl); - assertThat(GlobalMeterProvider.get()).isSameAs(globalMeterProvider); - + // the meter provider should have changed, but the tracer provider not + assertThat(openTelemetryController.getMeterProvider()).isNotSameAs(meterProvider); + assertThat(globalMeterProvider).isNotSameAs(GlobalOpenTelemetry.getMeterProvider()); + assertThat(openTelemetryController.getTracerProvider()).isSameAs(tracerProvider); + // the global tracer provider should have changed as we have rebuilt the OpenTelemetrySdk, which then creates a new ObfuscatedTracerProvider + assertThat(globalTracerProvider).isNotSameAs(GlobalOpenTelemetry.getTracerProvider()); } @Test void testShutdown() { openTelemetryController.shutdown(); - assertThat(null == GlobalMeterProvider.get()); + assertThat(null == GlobalOpenTelemetry.getMeterProvider()); } /** @@ -305,4 +322,83 @@ public String getName() { } } + /** + * Test changes in MetricsExporterSettings, which will lead to {@link SdkMeterProvider} being rebuilt and re-registered to {@link OpenTelemetryImpl} + */ + @Nested + static class ChangeMetrics extends SpringTestBase { + + @Autowired + OpenTelemetryControllerImpl openTelemetryController; + + private static CloseableHttpClient testClient; + + @BeforeAll + private static void initTestClient() { + RequestConfig.Builder requestBuilder = RequestConfig.custom(); + requestBuilder = requestBuilder.setConnectTimeout(1000); + requestBuilder = requestBuilder.setConnectionRequestTimeout(1000); + + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setDefaultRequestConfig(requestBuilder.build()); + testClient = builder.build(); + } + + @AfterAll + static void closeClient() throws Exception { + testClient.close(); + } + + void assertGet200(String url) throws Exception { + CloseableHttpResponse response = testClient.execute(new HttpGet(url)); + int statusCode = response.getStatusLine().getStatusCode(); + assertThat(statusCode).isEqualTo(200); + response.close(); + } + + void assertUnavailable(String url) { + Throwable throwable = catchThrowable(() -> testClient.execute(new HttpGet(url)) + .getStatusLine() + .getStatusCode()); + + assertThat(throwable).isInstanceOf(IOException.class); + } + + @Test + void testChangeMetricsExporterServices() throws Exception { + + SdkMeterProvider sdkMeterProvider = openTelemetryController.getMeterProvider(); + // enable prometheus and logging + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.metrics.prometheus.enabled", true); + properties.setProperty("inspectit.exporters.metrics.logging.enabled", true); + }); + // wait until the OpenTelemetryController has been reconfigured + SdkMeterProvider newSdkMeterProvider = openTelemetryController.getMeterProvider(); + // meter provider should have changed + assertThat(sdkMeterProvider).isNotSameAs(newSdkMeterProvider); + // Prometheus should be running + assertGet200("http://localhost:8888/metrics"); + + // disable prometheus + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.metrics.prometheus.enabled", false); + }); + assertUnavailable("http://localhost:8888/metrics"); + + // wait until the SdkMeterProvider has been rebuilt + Awaitility.await() + .atMost(15, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(newSdkMeterProvider).isNotSameAs(openTelemetryController.getMeterProvider())); + + // enable prometheus + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.metrics.prometheus.enabled", true); + }); + assertGet200("http://localhost:8888/metrics"); + + } + } + } \ No newline at end of file From a7f915b70c4cfc683bae1db5e60c2e6577bee59a Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 31 Jan 2022 09:18:27 +0100 Subject: [PATCH 59/86] #1279: Removed unused classes and comments/TODO statements --- .../core/exporter/JaegerExporterService.java | 13 ++--- .../CustomMetricReaderFactory.java | 44 ---------------- .../OpenTelemetryControllerImpl.java | 52 +++++++------------ 3 files changed, 23 insertions(+), 86 deletions(-) delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/CustomMetricReaderFactory.java diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java index d92038f1dc..eef7c7b758 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java @@ -12,7 +12,7 @@ import javax.validation.Valid; /** - * Service for the Jaeger OpenTelemetry exporter. + * Service for the {@link JaegerThriftSpanExporter}. * Can be dynamically started and stopped using the exporters.trace.jaeger.enabled configuration. */ @Component @@ -51,12 +51,7 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { protected boolean doEnable(InspectitConfig configuration) { try { JaegerExporterSettings settings = configuration.getExporters().getTracing().getJaeger(); - log.info("Starting Jaeger Exporter with url '{}' (grpc '{}')", settings.getUrl(), settings.getGrpc()); - - // TODO: use getUrl() or getGRPC()? - /*grpcSpanExporter = JaegerGrpcSpanExporter.builder() - .setEndpoint(settings.getGrpc() == null ? settings.getUrl() : settings.getGrpc()) - .build();*/ + log.info("Starting Jaeger Thrift Exporter with url '{}' (grpc '{}')", settings.getUrl(), settings.getGrpc()); // create span exporter spanExporter = JaegerThriftSpanExporter.builder().setEndpoint(settings.getUrl()).build(); @@ -73,14 +68,14 @@ protected boolean doEnable(InspectitConfig configuration) { @Override protected boolean doDisable() { - log.info("Stopping Jaeger Exporter"); + log.info("Stopping Jaeger Thrift Exporter"); try { openTelemetryController.unregisterTraceExporterService(this); if (null != spanExporter) { spanExporter.close(); } } catch (Throwable t) { - log.error("Error disabling Jaeger exporter", t); + log.error("Error disabling Jaeger Thrift Exporter", t); } return true; } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/CustomMetricReaderFactory.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/CustomMetricReaderFactory.java deleted file mode 100644 index 474f6b30e6..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/CustomMetricReaderFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -package rocks.inspectit.ocelot.core.opentelemetry; - -import io.opentelemetry.sdk.metrics.export.MetricProducer; -import io.opentelemetry.sdk.metrics.export.MetricReader; -import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; -import lombok.extern.slf4j.Slf4j; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Slf4j -public class CustomMetricReaderFactory implements MetricReaderFactory { - - private Map readerFactories = new ConcurrentHashMap<>(); - - @Override - public MetricReader apply(MetricProducer producer) { - return null; - } - - public boolean registerMetricReaderFactory(String registerName, MetricReaderFactory factory) { - // TODO: support immediate replacement? or do we need to unregister it before? - if (null != readerFactories.put(registerName, factory)) { - log.error("A MetricReaderFactory with the name '{}' was already registered.", registerName); - return false; - } else { - log.info("Successfully registered the '{}' MetricReaderFactory ({})", registerName, factory.getClass() - .getName()); - return true; - } - } - - public boolean unregisterMetricReaderFactory(String registerName) { - MetricReaderFactory metricReaderFactory = readerFactories.remove(registerName); - if (null == metricReaderFactory) { - log.error("Could not unregister '{}' as no such MetricReaderFactory was registered", registerName); - return false; - } else { - log.info("Successfully unregistered the '{}' MetricReaderFactory ({}}", registerName, metricReaderFactory.getClass() - .getName()); - return true; - } - } -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index 30f8826f8a..14d94c1d23 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -74,7 +74,6 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { * whether {@link GlobalOpenTelemetry} has been successfully been configured. */ @Getter - // TODO: make sure that configured is set accordingly when OTEL is being reconfigured (false while configuring, true after success) private boolean configured = false; /** @@ -261,7 +260,7 @@ public void flush() { } /** - * Shuts down the {@link OpenTelemetryControllerImpl} by calling {@link OpenTelemetryImpl#close()} and {@link MeterProviderImpl#close()} and waits for it to complete. + * Shuts down the {@link OpenTelemetryControllerImpl} by calling {@link OpenTelemetryImpl#close()} and waits for it to complete. * The shutdown is final, i.e., once this {@link OpenTelemetryImpl} is shutdown, it cannot be re-enabled! *

* Only use this method for testing or when the JVM is shutting down. @@ -324,7 +323,21 @@ void initOtel(InspectitConfig configuration) { .getServiceName())); // build new SdkTracerProvider - tracerProvider = buildSdkTracerProvider(configuration); + double sampleProbability = configuration.getTracing().getSampleProbability(); + // set up sampler + sampler = new DynamicSampler(sampleProbability); + // set up spanProcessor and spanExporter + spanExporter = DynamicMultiSpanExporter.create(); + spanProcessor = BatchSpanProcessor.builder(spanExporter) + .setMaxExportBatchSize(configuration.getTracing().getMaxExportBatchSize()) + .setScheduleDelay(configuration.getTracing().getScheduleDelayMillis(), TimeUnit.MILLISECONDS) + .build(); + // build TracerProvider + SdkTracerProviderBuilder builder = SdkTracerProvider.builder() + .setSampler(sampler) + .setResource(serviceNameResource) + .addSpanProcessor(spanProcessor); + tracerProvider = builder.build(); // build new SdkMeterProvider meterProvider = SdkMeterProvider.builder().setResource(serviceNameResource).build(); @@ -344,13 +357,14 @@ void initOtel(InspectitConfig configuration) { if (null != OpenTelemetryUtils.getGlobalOpenTelemetry()) { log.info("reset {}", GlobalOpenTelemetry.get().getClass().getName()); GlobalOpenTelemetry.resetForTest(); + // update the OTEL_TRACER field in OpenTelemetrySpanBuilderImpl in case that it was already set + OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); } // set GlobalOpenTelemetry openTelemetry.registerGlobal(); - // update the OTEL_TRACER field in OpenTelemetrySpanBuilderImpl in case that it was already set - OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); + } /** @@ -376,34 +390,6 @@ synchronized SdkTracerProvider configureTracing(InspectitConfig configuration) { } } - /** - * Builds a new {@link SdkTracerProvider} based on the given {@link InspectitConfig} - * - * @param configuration - * - * @return A new {@link SdkTracerProvider} - */ - private synchronized SdkTracerProvider buildSdkTracerProvider(InspectitConfig configuration) { - double sampleProbability = configuration.getTracing().getSampleProbability(); - - // set up sampler - sampler = new DynamicSampler(sampleProbability); - - // set up spanProcessor and spanExporter - spanExporter = DynamicMultiSpanExporter.create(); - spanProcessor = BatchSpanProcessor.builder(spanExporter) - .setMaxExportBatchSize(configuration.getTracing().getMaxExportBatchSize()) - .setScheduleDelay(configuration.getTracing().getScheduleDelayMillis(), TimeUnit.MILLISECONDS) - .build(); - // build TracerProvider - SdkTracerProviderBuilder builder = SdkTracerProvider.builder() - .setSampler(sampler) - .setResource(serviceNameResource) - .addSpanProcessor(spanProcessor); - - return builder.build(); - } - /** * Configures the {@link SdkMeterProvider} * From e57ac2a1e93426077e4a0cbfec977776e71b1e74 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 1 Feb 2022 09:28:17 +0100 Subject: [PATCH 60/86] #1279: Added test cases to OpenTelemetryControllerImplTest --- .../OpenTelemetryControllerImpl.java | 2 - .../OpenTelemetryControllerImplTest.java | 70 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index 14d94c1d23..a2af1c3998 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -363,8 +363,6 @@ void initOtel(InspectitConfig configuration) { // set GlobalOpenTelemetry openTelemetry.registerGlobal(); - - } /** diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java index cc1e1214da..24575d77ed 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java @@ -1,10 +1,15 @@ package rocks.inspectit.ocelot.core.opentelemetry; +import io.github.netmikey.logunit.api.LogCapturer; +import io.opencensus.trace.Tracing; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.TracerProvider; +import io.opentelemetry.context.Scope; import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; @@ -19,6 +24,7 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.Spy; @@ -30,6 +36,8 @@ import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableTraceExporterService; +import rocks.inspectit.ocelot.core.exporter.LoggingTraceExporterService; +import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; import java.io.IOException; import java.lang.reflect.Method; @@ -399,6 +407,68 @@ void testChangeMetricsExporterServices() throws Exception { assertGet200("http://localhost:8888/metrics"); } + + @RegisterExtension + LogCapturer spanLogs = LogCapturer.create().captureForType(LoggingSpanExporter.class); + + @Autowired + LoggingTraceExporterService loggingTraceExporterService; + + /** + * Verify that the {@link io.opencensus.trace.Tracer} in {@link Tracing#getTracer()} is correctly set to {@link GlobalOpenTelemetry#getTracerProvider()} + * + * @throws InterruptedException + */ + @Test + void testChangeTracingExporterServices() throws InterruptedException { + SdkTracerProvider sdkTracerProvider = openTelemetryController.getTracerProvider(); + // enable logging + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.tracing.logging.enabled", true); + }); + assertThat(loggingTraceExporterService.isEnabled()).isTrue(); + // make OC spans and flush + makeOCSpansAndFlush("test-span"); + // verify the spans are logged + Awaitility.waitAtMost(5, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> assertThat(spanLogs.size()).isEqualTo(1)); + assertThat(sdkTracerProvider).isEqualTo(openTelemetryController.getTracerProvider()); + + // shut off tracer + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.tracing.logging.enabled", false); + }); + assertThat(loggingTraceExporterService.isEnabled()).isFalse(); + // make OC spans and flush + makeOCSpansAndFlush("ignored-span"); + // verify that no more spans are logged + Thread.sleep(5000); + assertThat(spanLogs.size()).isEqualTo(1); + } + + private static void makeOtelSpansAndFlush(String spanName) { + // build and flush span + Span span = GlobalOpenTelemetry.getTracerProvider() + .get("rocks.inspectit.instrumentation.test") + .spanBuilder(spanName) + .startSpan(); + try (Scope scope = span.makeCurrent()) { + } finally { + span.end(); + } + OpenTelemetryUtils.flush(); + } + + private static void makeOCSpansAndFlush(String spanName) { + // get OC tracer and start spans + io.opencensus.trace.Tracer tracer = Tracing.getTracer(); + + // start span + try (io.opencensus.common.Scope scope = tracer.spanBuilder(spanName).startScopedSpan()) { + io.opencensus.trace.Span span = tracer.getCurrentSpan(); + span.addAnnotation("anno"); + } + OpenTelemetryUtils.flush(); + } } } \ No newline at end of file From a32ad6fdf500247481d4c8f5b04cbdcb4c192ba0 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 1 Feb 2022 09:52:32 +0100 Subject: [PATCH 61/86] #1279: Minor refactoring. --- .../opentelemetry/OpenTelemetryControllerImpl.java | 10 ++++------ .../ocelot/core/opentelemetry/OpenTelemetryImpl.java | 3 ++- .../exporter/LoggingTraceExporterServiceIntTest.java | 2 +- .../opentelemetry/OpenTelemetryControllerImplTest.java | 6 ++++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index a2af1c3998..7467f40c13 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -349,20 +349,18 @@ void initOtel(InspectitConfig configuration) { .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) .build(); - // if the OpenTelemetryImpl has not been build, then build and register it + // build and register OpenTelemetryImpl openTelemetry = OpenTelemetryImpl.builder().openTelemetry(openTelemetrySdk).build(); - // check if any OpenTelemetry has been registered to GlobalOpenTelemetry. // If so, reset it. if (null != OpenTelemetryUtils.getGlobalOpenTelemetry()) { log.info("reset {}", GlobalOpenTelemetry.get().getClass().getName()); GlobalOpenTelemetry.resetForTest(); - // update the OTEL_TRACER field in OpenTelemetrySpanBuilderImpl in case that it was already set - OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); } - - // set GlobalOpenTelemetry openTelemetry.registerGlobal(); + + // update the OTEL_TRACER field in OpenTelemetrySpanBuilderImpl in case that it was already set + OpenCensusShimUtils.updateOpenTelemetryTracerInOpenTelemetrySpanBuilderImpl(); } /** diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java index ec3dbc880b..4cb5e78d55 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java @@ -141,11 +141,12 @@ public void flush() { } /** - * Registers {@link OpenTelemetryImpl this} to {@link GlobalOpenTelemetry#set(OpenTelemetry)} + * Registers {@link OpenTelemetryImpl this} to {@link GlobalOpenTelemetry#set(OpenTelemetry)}. * * @return */ public OpenTelemetryImpl registerGlobal() { + // register globally GlobalOpenTelemetry.set(this); return this; } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java index ac5e4b3f86..821a7af005 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java @@ -174,7 +174,7 @@ void verifyOpenCensusTraceSent() throws InterruptedException { makeSpansAndFlush(); // assert that both spans are logged - assertThat(spanLogs.getEvents()).hasSize(2); + Awaitility.waitAtMost(5, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> assertThat(spanLogs.getEvents()).hasSize(2)); assertThat(spanLogs.getEvents().get(0).getMessage()).contains("openCensusChild"); int numEvents = spanLogs.size(); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java index 24575d77ed..57ef0a7ab9 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java @@ -430,7 +430,9 @@ void testChangeTracingExporterServices() throws InterruptedException { // make OC spans and flush makeOCSpansAndFlush("test-span"); // verify the spans are logged - Awaitility.waitAtMost(5, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> assertThat(spanLogs.size()).isEqualTo(1)); + Awaitility.waitAtMost(5, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(spanLogs.getEvents()).hasSize(1)); assertThat(sdkTracerProvider).isEqualTo(openTelemetryController.getTracerProvider()); // shut off tracer @@ -442,7 +444,7 @@ void testChangeTracingExporterServices() throws InterruptedException { makeOCSpansAndFlush("ignored-span"); // verify that no more spans are logged Thread.sleep(5000); - assertThat(spanLogs.size()).isEqualTo(1); + assertThat(spanLogs.getEvents()).hasSize(1); } private static void makeOtelSpansAndFlush(String spanName) { From 50b056a97aad2cda74eb39a476692d3b0e4a38f1 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Tue, 8 Feb 2022 11:24:26 +0100 Subject: [PATCH 62/86] #1297: started implementing JaegerGrpcExporterService and started working on OtlpGrpcTraceExporterServiceIntTest --- gradle.properties | 3 +- .../trace/JaegerGrpcExporterSettings.java | 5 + .../trace/TraceExportersSettings.java | 2 + inspectit-ocelot-core/build.gradle | 4 +- .../exporter/JaegerGrpcExporterService.java | 79 ++++++++++++++++ .../OtlpGrpcTraceExporterServiceIntTest.java | 91 +++++++++++++++++++ 6 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerGrpcExporterSettings.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java diff --git a/gradle.properties b/gradle.properties index 6ae4e690d0..fcf050f35a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,4 +7,5 @@ springVersion=2.1.1.RELEASE prometheusClientVersion=0.6.0 # appropriate netty version, see https://github.com/census-instrumentation/opencensus-java/blob/master/exporters/trace/ocagent/README.md tcnativeVersion=2.0.20.Final -mockitoVersion=4.1.0 \ No newline at end of file +mockitoVersion=4.1.0 +grpcVersion=1.43.1 \ No newline at end of file diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerGrpcExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerGrpcExporterSettings.java new file mode 100644 index 0000000000..208bdb78b7 --- /dev/null +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerGrpcExporterSettings.java @@ -0,0 +1,5 @@ +package rocks.inspectit.ocelot.config.model.exporters.trace; + +public class JaegerGrpcExporterSettings extends JaegerExporterSettings { + +} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java index 85343a0638..001232c6f9 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java @@ -12,6 +12,8 @@ public class TraceExportersSettings { @Valid private JaegerExporterSettings jaeger; + @Valid JaegerGrpcExporterSettings jaegerGrpc; + @Valid private ZipkinExporterSettings zipkin; diff --git a/inspectit-ocelot-core/build.gradle b/inspectit-ocelot-core/build.gradle index 3ddd966985..3cd69c095a 100644 --- a/inspectit-ocelot-core/build.gradle +++ b/inspectit-ocelot-core/build.gradle @@ -122,8 +122,8 @@ dependencies { 'org.assertj:assertj-guava:3.2.1', 'org.awaitility:awaitility:3.1.5', "com.github.tomakehurst:wiremock-jre8:2.21.0", - "io.grpc:grpc-netty-shaded:1.20.0", - "io.grpc:grpc-stub:1.20.0", + "io.grpc:grpc-netty-shaded:${grpcVersion}", + "io.grpc:grpc-stub:${grpcVersion}", 'org.influxdb:influxdb-java:2.15', 'io.apisense.embed.influx:embed-influxDB:1.2.1' ) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java new file mode 100644 index 0000000000..81a2f2b7ac --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java @@ -0,0 +1,79 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerExporterSettings; + +import javax.validation.Valid; + +/** + * Service for the {@link JaegerGrpcSpanExporter} exporter. + * Can be dynamically started and stopped using the exporters.trace.jaeger-grpc.enabled configuration. + */ +@Component +@Slf4j +public class JaegerGrpcExporterService extends DynamicallyActivatableTraceExporterService { + + private JaegerGrpcSpanExporter spanExporter; + + public JaegerGrpcExporterService() { + super("exporters.tracing.jaegerGrpc", "tracing.enabled"); + } + + @Override + public SpanExporter getSpanExporter() { + return null; + } + + @Override + protected boolean checkEnabledForConfig(InspectitConfig conf) { + @Valid JaegerExporterSettings jaeger = conf.getExporters().getTracing().getJaegerGrpc(); + if (conf.getTracing().isEnabled() && jaeger.isEnabled()) { + if (!StringUtils.isEmpty(jaeger.getGrpc())) { + return true; + } else if (StringUtils.isNotEmpty(jaeger.getUrl())) { + // print warning if user used wrong setup + log.warn("In order to use Jaeger Thrift span exporter, please specify the gRPC URL endpoint property instead of the HTTP URL."); + } + } + return false; + } + + @Override + protected boolean doEnable(InspectitConfig configuration) { + try { + JaegerExporterSettings settings = configuration.getExporters().getTracing().getJaegerGrpc(); + log.info("Starting Jaeger gRPC Exporter with grpc '{}'", settings.getGrpc()); + + spanExporter = JaegerGrpcSpanExporter.builder() + .setEndpoint(settings.getGrpc() == null ? settings.getUrl() : settings.getGrpc()) + .build(); + + // register + openTelemetryController.registerTraceExporterService(this); + + return true; + } catch (Throwable t) { + log.error("Error creating Jaeger gRPC Exporter", t); + return false; + } + } + + @Override + protected boolean doDisable() { + log.info("Stopping Jaeger gRPC Exporter"); + try { + openTelemetryController.unregisterTraceExporterService(this); + if (null != spanExporter) { + spanExporter.close(); + } + } catch (Throwable t) { + log.error("Error disabling Jaeger gRPC Exporter", t); + } + return true; + } +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java new file mode 100644 index 0000000000..6a111c2075 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java @@ -0,0 +1,91 @@ +package rocks.inspectit.ocelot.core.exporter; + +import com.github.tomakehurst.wiremock.WireMockServer; +import io.github.netmikey.logunit.api.LogCapturer; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Scope; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; +import rocks.inspectit.ocelot.bootstrap.Instances; +import rocks.inspectit.ocelot.core.SpringTestBase; + +import java.util.concurrent.TimeUnit; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +@TestPropertySource(properties = {"inspectit.exporters.tracing.logging.enabled=true", "inspectit.tracing.max-export-batch-size=1"}) +@DirtiesContext +public class OtlpGrpcTraceExporterServiceIntTest extends SpringTestBase { + + private static final Logger logger = LoggerFactory.getLogger(OtlpGrpcTraceExporterService.class); + + @RegisterExtension + LogCapturer spanLogs = LogCapturer.create().captureForType(LoggingSpanExporter.class); + + public static final int OTLP_GRPC_PORT = 4318; + + public static final String OTLP_GRPC_PATH = "/v1/trace"; + + private WireMockServer wireMockServer; + + @Autowired + private OtlpGrpcTraceExporterService service; + + @DirtiesContext + @BeforeEach + void enableService() { + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.tracing.otlpGrpc.url", String.format("http://127.0.0.1:%d%s", OTLP_GRPC_PORT, OTLP_GRPC_PATH)); + properties.setProperty("inspectit.exporters.tracing.otlpGrpc.enabled", true); + }); + await().atMost(10, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + } + + @BeforeEach + void setupWiremock() { + wireMockServer = new WireMockServer(options().port(OTLP_GRPC_PORT)); + wireMockServer.start(); + configureFor(wireMockServer.port()); + + stubFor(post(urlPathEqualTo(OTLP_GRPC_PATH)).willReturn(aResponse().withStatus(200))); + } + + @AfterEach + void cleanup() { + wireMockServer.stop(); + } + + @Test + void verifyTraceSent() throws InterruptedException { + Span span = GlobalOpenTelemetry.getTracer("rocks.inspectit.instrumentation", "0.0.1") + .spanBuilder("otlp-grpc-span") + .startSpan(); + try (Scope sc = span.makeCurrent()) { + } finally { + span.end(); + } + Instances.openTelemetryController.flush(); + + logger.info("Wait for OTLP to process the span..."); + await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> assertThat(spanLogs.size()).isGreaterThan(0)); + Thread.sleep(1100L); + + await().atMost(15, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { + verify(postRequestedFor(urlPathEqualTo(OTLP_GRPC_PATH))); + }); + } +} From 943ba81521eb4fa0b876f061d2eaee9787426daf Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Wed, 9 Mar 2022 16:15:10 +0100 Subject: [PATCH 63/86] feat(open-telemetry): [#1297] add test base for exporter service integration tests; fix/add tests `JaegerGrpcExporterServiceIntTest.java` and `OtlpGrpcTraceExporterServiceIntTest.java` --- .../ocelot/config/default/exporters.yml | 4 +- inspectit-ocelot-core/build.gradle | 13 +- .../exporter/JaegerGrpcExporterService.java | 10 +- .../ExporterServiceIntegrationTestBase.java | 223 ++++++++++++++++++ .../JaegerGrpcExporterServiceIntTest.java | 36 +++ ...aegerThriftHttpExporterServiceIntTest.java | 41 ++++ .../OtlpGrpcTraceExporterServiceIntTest.java | 77 +----- .../src/test/resources/otel-config.yaml | 46 ++++ 8 files changed, 376 insertions(+), 74 deletions(-) create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ExporterServiceIntegrationTestBase.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java create mode 100644 inspectit-ocelot-core/src/test/resources/otel-config.yaml diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml index d8baead956..fe0a1450f4 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml @@ -58,7 +58,9 @@ inspectit: enabled: true # the URL under which the jaeger thrift server can be accessed, e.g. http://127.0.0.1:14268/api/traces url: null - + # settings for Jaeger gRPC exporter + jaeger-grpc: + enabled: false # settings for the LoggingSpanExporter used in LoggingTraceExporterService logging: diff --git a/inspectit-ocelot-core/build.gradle b/inspectit-ocelot-core/build.gradle index 3cd69c095a..4e553f7bfb 100644 --- a/inspectit-ocelot-core/build.gradle +++ b/inspectit-ocelot-core/build.gradle @@ -110,6 +110,7 @@ dependencies { "io.opentelemetry:opentelemetry-opencensus-shim", "io.opentelemetry:opentelemetry-semconv", "io.opentelemetry:opentelemetry-sdk-testing", + "io.opentelemetry:opentelemetry-proto:1.7.1-alpha", 'org.springframework:spring-test:5.1.3.RELEASE', // to make use of SpyBean @@ -125,7 +126,17 @@ dependencies { "io.grpc:grpc-netty-shaded:${grpcVersion}", "io.grpc:grpc-stub:${grpcVersion}", 'org.influxdb:influxdb-java:2.15', - 'io.apisense.embed.influx:embed-influxDB:1.2.1' + 'io.apisense.embed.influx:embed-influxDB:1.2.1', + + // for docker test containers + 'org.testcontainers:testcontainers:1.15.2', + 'org.testcontainers:junit-jupiter:1.15.2', + + // ServerExtension + 'com.linecorp.armeria:armeria-junit5:1.14.1', + 'com.linecorp.armeria:armeria-grpc-protocol:1.14.1', + + 'com.google.protobuf:protobuf-java-util:3.15.7', ) jmh( diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java index 81a2f2b7ac..d37ccc8a3e 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java @@ -1,7 +1,7 @@ package rocks.inspectit.ocelot.core.exporter; import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter; -import io.opentelemetry.sdk.trace.export.SpanExporter; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -18,17 +18,13 @@ @Slf4j public class JaegerGrpcExporterService extends DynamicallyActivatableTraceExporterService { + @Getter private JaegerGrpcSpanExporter spanExporter; public JaegerGrpcExporterService() { super("exporters.tracing.jaegerGrpc", "tracing.enabled"); } - @Override - public SpanExporter getSpanExporter() { - return null; - } - @Override protected boolean checkEnabledForConfig(InspectitConfig conf) { @Valid JaegerExporterSettings jaeger = conf.getExporters().getTracing().getJaegerGrpc(); @@ -37,7 +33,7 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { return true; } else if (StringUtils.isNotEmpty(jaeger.getUrl())) { // print warning if user used wrong setup - log.warn("In order to use Jaeger Thrift span exporter, please specify the gRPC URL endpoint property instead of the HTTP URL."); + log.warn("In order to use Jaeger gRPC span exporter, please specify the gRPC URL endpoint property instead of the HTTP URL."); } } return false; diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ExporterServiceIntegrationTestBase.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ExporterServiceIntegrationTestBase.java new file mode 100644 index 0000000000..044067ab12 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ExporterServiceIntegrationTestBase.java @@ -0,0 +1,223 @@ +package rocks.inspectit.ocelot.core.exporter; + +import com.google.protobuf.InvalidProtocolBufferException; +import com.linecorp.armeria.server.ServerBuilder; +import com.linecorp.armeria.server.ServiceRequestContext; +import com.linecorp.armeria.server.grpc.protocol.AbstractUnaryGrpcService; +import com.linecorp.armeria.testing.junit5.server.ServerExtension; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest; +import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse; +import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest; +import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse; +import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; +import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import rocks.inspectit.ocelot.bootstrap.Instances; +import rocks.inspectit.ocelot.core.SpringTestBase; + +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.testcontainers.Testcontainers.exposeHostPorts; + +/** + * Base class for exporter integration tests. Verifies integration with the OpenTelemetry Collector. The Collector can be configured to accept the required data over gRPC or HTTP and exports the data over gRPC to a server running in process, allowing assertions to be made against the data. + * This class is based on the {@link io.opentelemetry.integrationtest.OtlpExporterIntegrationTest} + */ +@Testcontainers(disabledWithoutDocker = true) +abstract class ExporterServiceIntegrationTestBase extends SpringTestBase { + + static final String COLLECTOR_IMAGE = "ghcr.io/open-telemetry/opentelemetry-java/otel-collector"; + + static final Integer COLLECTOR_OTLP_GRPC_PORT = 4317; + + static final Integer COLLECTOR_OTLP_HTTP_PORT = 4318; + + static final Integer COLLECTOR_HEALTH_CHECK_PORT = 13133; + + static final Integer COLLECTOR_JAEGER_GRPC_PORT = 14250; + + static final Integer COLLECTOR_JAEGER_THRIFT_HTTP_PORT = 14268; + + static final Integer COLLECTOR_JAEGER_THRIFT_BINARY_PORT = 6832; + + static final Integer COLLECTOR_JAEGER_THRIFT_COMPACT_PORT = 6831; + + static final Integer COLLECTOR_PROMETHEUS_PORT = 8888; + + static final String INSTRUMENTATION_NAME = "rocks.inspectit.ocelot.instrumentation"; + + static final String INSTRUMENTATION_VERSION = "0.0.1"; + + static final Resource RESOURCE = Resource.getDefault() + .toBuilder() + .put(ResourceAttributes.SERVICE_NAME, "OTEL integration test") + .build(); + + private static final Logger LOGGER = Logger.getLogger(ExporterServiceIntegrationTestBase.class.getName()); + + /** + * The {@link OtlpGrpcServer} used as an exporter endpoirt for the OpenTelemetry Collector + */ + static OtlpGrpcServer grpcServer; + + /** + * The OpenTelemetry Collector + */ + static GenericContainer collector; + + @BeforeAll + static void startCollector() { + + // start the gRPC server + grpcServer = new OtlpGrpcServer(); + grpcServer.start(); + + // Expose the port the in-process OTLP gRPC server will run on before the collector is + // initialized so the collector can connect to it. + exposeHostPorts(grpcServer.httpPort()); + + collector = new GenericContainer<>(DockerImageName.parse(COLLECTOR_IMAGE)).withEnv("LOGGING_EXPORTER_LOG_LEVEL", "INFO") + .withEnv("OTLP_EXPORTER_ENDPOINT", "host.testcontainers.internal:" + grpcServer.httpPort()) + .withEnv("PROMETHEUS_SCRAPE_TARGET", String.format("host.testcontainers.internal:%s", COLLECTOR_PROMETHEUS_PORT)) + .withClasspathResourceMapping("otel-config.yaml", "/otel-config.yaml", BindMode.READ_ONLY) + .withCommand("--config", "/otel-config.yaml") + .withLogConsumer(outputFrame -> LOGGER.log(Level.INFO, outputFrame.getUtf8String().replace("\n", ""))) + // expose all relevant ports + .withExposedPorts(COLLECTOR_OTLP_GRPC_PORT, COLLECTOR_OTLP_HTTP_PORT, COLLECTOR_HEALTH_CHECK_PORT, COLLECTOR_JAEGER_THRIFT_HTTP_PORT, COLLECTOR_JAEGER_THRIFT_BINARY_PORT, COLLECTOR_JAEGER_THRIFT_COMPACT_PORT, COLLECTOR_JAEGER_GRPC_PORT, COLLECTOR_PROMETHEUS_PORT) + .waitingFor(Wait.forHttp("/").forPort(COLLECTOR_HEALTH_CHECK_PORT)); + + // collector.withStartupTimeout(Duration.of(1, ChronoUnit.MINUTES)); + // note: in case you receive the 'Caused by: org.testcontainers.containers.ContainerLaunchException: Timed out waiting for container port to open' exception, + // uncomment the above line. The exception is probably caused by Docker Desktop hiccups and should only appear locally. + collector.start(); + } + + @AfterAll + static void stop() { + grpcServer.stop().join(); + collector.stop(); + } + + @BeforeEach + void reset() { + grpcServer.reset(); + } + + /** + * The current {@link GlobalOpenTelemetry#getTracer(String)} + * + * @return The {@link Tracer} registered at {@link GlobalOpenTelemetry} + */ + static Tracer getTracer() { + return GlobalOpenTelemetry.getTracer(INSTRUMENTATION_NAME, INSTRUMENTATION_VERSION); + } + + /** + * Gets the desired endpoint of the {@link #collector} constructed as 'http://{@link GenericContainer#getHost() collector.getHost()}:{@link GenericContainer#getMappedPort(int) collector.getMappedPort(port)}/path' + * + * @param originalPort the port to get the actual mapped port for + * @param path the path + * + * @return the constructed endpoint for the {@link #collector} + */ + static String getEndpoint(Integer originalPort, String path) { + return String.format("http://%s:%d/%s", collector.getHost(), collector.getMappedPort(originalPort), path.startsWith("/") ? path.substring(1) : path); + } + + /** + * Creates a nested trace with parent and child span and flushes them. + */ + void makeSpansAndFlush() { + // start span and nested span + Span parentSpan = getTracer().spanBuilder("openTelemetryParentSpan").startSpan(); + try (Scope scope = parentSpan.makeCurrent()) { + Span childSpan = getTracer().spanBuilder("openTelemetryChildSpan").startSpan(); + try (Scope child = childSpan.makeCurrent()) { + // do sth + } finally { + childSpan.end(); + } + } finally { + parentSpan.end(); + } + + // flush pending spans + Instances.openTelemetryController.flush(); + } + + /** + * OpenTelemetry Protocol gRPC Server + */ + static class OtlpGrpcServer extends ServerExtension { + + final List traceRequests = new ArrayList<>(); + + final List metricRequests = new ArrayList<>(); + + final List logRequests = new ArrayList<>(); + + private void reset() { + traceRequests.clear(); + metricRequests.clear(); + logRequests.clear(); + } + + @Override + protected void configure(ServerBuilder sb) { + sb.service("/opentelemetry.proto.collector.trace.v1.TraceService/Export", new AbstractUnaryGrpcService() { + @Override + protected CompletionStage handleMessage(ServiceRequestContext ctx, byte[] message) { + try { + traceRequests.add(ExportTraceServiceRequest.parseFrom(message)); + } catch (InvalidProtocolBufferException e) { + throw new UncheckedIOException(e); + } + return completedFuture(ExportTraceServiceResponse.getDefaultInstance().toByteArray()); + } + }); + sb.service("/opentelemetry.proto.collector.metrics.v1.MetricsService/Export", new AbstractUnaryGrpcService() { + @Override + protected CompletionStage handleMessage(ServiceRequestContext ctx, byte[] message) { + try { + metricRequests.add(ExportMetricsServiceRequest.parseFrom(message)); + } catch (InvalidProtocolBufferException e) { + throw new UncheckedIOException(e); + } + return completedFuture(ExportMetricsServiceResponse.getDefaultInstance().toByteArray()); + } + }); + sb.service("/opentelemetry.proto.collector.logs.v1.LogsService/Export", new AbstractUnaryGrpcService() { + @Override + protected CompletionStage handleMessage(ServiceRequestContext ctx, byte[] message) { + try { + logRequests.add(ExportLogsServiceRequest.parseFrom(message)); + } catch (InvalidProtocolBufferException e) { + throw new UncheckedIOException(e); + } + return completedFuture(ExportLogsServiceResponse.getDefaultInstance().toByteArray()); + } + }); + sb.http(0); + } + } + +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java new file mode 100644 index 0000000000..7e2e9d28a2 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java @@ -0,0 +1,36 @@ +package rocks.inspectit.ocelot.core.exporter; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class JaegerGrpcExporterServiceIntTest extends ExporterServiceIntegrationTestBase { + + public static String JAEGER_GRPC_PATH = "/v1/traces"; + + @Autowired + JaegerGrpcExporterService service; + + @Test + @DirtiesContext + void verifyTraceSent() { + updateProperties(mps -> { + mps.setProperty("inspectit.exporters.tracing.jaeger-grpc.enabled", true); + mps.setProperty("inspectit.exporters.tracing.jaeger-grpc.grpc", getEndpoint(COLLECTOR_JAEGER_GRPC_PORT, JAEGER_GRPC_PATH)); + }); + await().atMost(15, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + + makeSpansAndFlush(); + + await().atMost(15, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(grpcServer.traceRequests).hasSize(1)); + } +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java new file mode 100644 index 0000000000..738499344b --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java @@ -0,0 +1,41 @@ +package rocks.inspectit.ocelot.core.exporter; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +@DirtiesContext +public class JaegerThriftHttpExporterServiceIntTest extends ExporterServiceIntegrationTestBase { + + public static final String JAEGER_PATH = "/api/traces"; + + @Autowired + JaegerExporterService service; + + @DirtiesContext + @Test + void verifyTraceSent() { + + // enable Jaeger Thrift exporter + updateProperties(mps -> { + mps.setProperty("inspectit.exporters.tracing.jaeger.enabled", true); + mps.setProperty("inspectit.exporters.tracing.jaeger.url", getEndpoint(COLLECTOR_JAEGER_THRIFT_HTTP_PORT, JAEGER_PATH)); + }); + await().atMost(15, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + + makeSpansAndFlush(); + + await().atMost(15, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(grpcServer.traceRequests).hasSize(1)); + + } + +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java index 6a111c2075..99e13e493c 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java @@ -1,91 +1,38 @@ package rocks.inspectit.ocelot.core.exporter; -import com.github.tomakehurst.wiremock.WireMockServer; -import io.github.netmikey.logunit.api.LogCapturer; -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Scope; -import io.opentelemetry.exporter.logging.LoggingSpanExporter; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.TestPropertySource; -import rocks.inspectit.ocelot.bootstrap.Instances; -import rocks.inspectit.ocelot.core.SpringTestBase; import java.util.concurrent.TimeUnit; -import static com.github.tomakehurst.wiremock.client.WireMock.*; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -@TestPropertySource(properties = {"inspectit.exporters.tracing.logging.enabled=true", "inspectit.tracing.max-export-batch-size=1"}) @DirtiesContext -public class OtlpGrpcTraceExporterServiceIntTest extends SpringTestBase { +public class OtlpGrpcTraceExporterServiceIntTest extends ExporterServiceIntegrationTestBase { - private static final Logger logger = LoggerFactory.getLogger(OtlpGrpcTraceExporterService.class); - - @RegisterExtension - LogCapturer spanLogs = LogCapturer.create().captureForType(LoggingSpanExporter.class); - - public static final int OTLP_GRPC_PORT = 4318; - - public static final String OTLP_GRPC_PATH = "/v1/trace"; - - private WireMockServer wireMockServer; + public static final String OTLP_GRPC_TRACING_PATH = "/v1/trace"; @Autowired private OtlpGrpcTraceExporterService service; - @DirtiesContext - @BeforeEach - void enableService() { + @Test + void verifyTraceSent() { updateProperties(properties -> { - properties.setProperty("inspectit.exporters.tracing.otlpGrpc.url", String.format("http://127.0.0.1:%d%s", OTLP_GRPC_PORT, OTLP_GRPC_PATH)); - properties.setProperty("inspectit.exporters.tracing.otlpGrpc.enabled", true); + properties.setProperty("inspectit.exporters.tracing.otlp-grpc.url", getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_GRPC_TRACING_PATH)); + properties.setProperty("inspectit.exporters.tracing.otlp-grpc.enabled", true); }); + + System.out.println("endpoint: " + getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_GRPC_TRACING_PATH)); await().atMost(10, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); - } - - @BeforeEach - void setupWiremock() { - wireMockServer = new WireMockServer(options().port(OTLP_GRPC_PORT)); - wireMockServer.start(); - configureFor(wireMockServer.port()); - stubFor(post(urlPathEqualTo(OTLP_GRPC_PATH)).willReturn(aResponse().withStatus(200))); - } - - @AfterEach - void cleanup() { - wireMockServer.stop(); - } - - @Test - void verifyTraceSent() throws InterruptedException { - Span span = GlobalOpenTelemetry.getTracer("rocks.inspectit.instrumentation", "0.0.1") - .spanBuilder("otlp-grpc-span") - .startSpan(); - try (Scope sc = span.makeCurrent()) { - } finally { - span.end(); - } - Instances.openTelemetryController.flush(); - - logger.info("Wait for OTLP to process the span..."); - await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> assertThat(spanLogs.size()).isGreaterThan(0)); - Thread.sleep(1100L); + makeSpansAndFlush(); - await().atMost(15, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { - verify(postRequestedFor(urlPathEqualTo(OTLP_GRPC_PATH))); - }); + await().atMost(15, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(grpcServer.traceRequests).hasSize(1)); } } diff --git a/inspectit-ocelot-core/src/test/resources/otel-config.yaml b/inspectit-ocelot-core/src/test/resources/otel-config.yaml new file mode 100644 index 0000000000..eb2fc09021 --- /dev/null +++ b/inspectit-ocelot-core/src/test/resources/otel-config.yaml @@ -0,0 +1,46 @@ +extensions: + health_check: {} +receivers: + + # accept Jaeger over all protocols + jaeger: + protocols: + grpc: + thrift_binary: + thrift_http: + thrift_compact: + + # accept OTLP over all protocols + otlp: + protocols: + grpc: + http: + + # accept Prometheus from port 8888 + prometheus: + config: + scrape_configs: + - job_name: 'otel-collector' + scrape_interval: 500ms + static_configs: + - targets: [$PROMETHEUS_SCRAPE_TARGET] + +exporters: + logging: + logLevel: $LOGGING_EXPORTER_LOG_lEVEL + otlp: + endpoint: $OTLP_EXPORTER_ENDPOINT + tls: + insecure: true +service: + extensions: [health_check] + pipelines: + metrics: + receivers: [otlp, prometheus] + exporters: [logging, otlp] + traces: + receivers: [otlp, jaeger] + exporters: [logging, otlp] + logs: + receivers: [otlp] + exporters: [logging, otlp] From 762553deeb96d710ee543e8da3eabb9c06c73fc7 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 10 Mar 2022 14:55:43 +0100 Subject: [PATCH 64/86] feat(open-telemetry): [#1297] improve test base for exporter service integration tests and add `OtlpGrpcMetricsExporterService` --- .../TraceExportersConfiguration.java | 30 ++--- .../exporters/JaegerExporterIntTest.java | 5 +- .../metrics/MetricsExportersSettings.java | 3 + .../OtlpGrpcMetricsExporterSettings.java | 29 +++++ .../trace/JaegerGrpcExporterSettings.java | 25 +++- .../trace/OtlpGrpcTraceExporterSettings.java | 4 +- .../ocelot/config/default/exporters.yml | 14 ++- inspectit-ocelot-core/build.gradle | 2 + .../exporter/JaegerGrpcExporterService.java | 10 +- .../LoggingMetricExporterService.java | 2 +- .../OtlpGrpcMetricsExporterService.java | 81 ++++++++++++ .../OtlpGrpcTraceExporterService.java | 3 - .../ExporterServiceIntegrationTestBase.java | 118 +++++++++++++++++- .../JaegerGrpcExporterServiceIntTest.java | 2 +- ...aegerThriftHttpExporterServiceIntTest.java | 2 +- ...OtlpGrpcMetricsExporterServiceIntTest.java | 45 +++++++ .../OtlpGrpcTraceExporterServiceIntTest.java | 7 +- .../ZipkinExporterServiceInt2Test.java | 38 ++++++ .../src/test/resources/otel-config.yaml | 12 +- 19 files changed, 386 insertions(+), 46 deletions(-) create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpGrpcMetricsExporterSettings.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterService.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java diff --git a/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java b/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java index c60fe17c3b..499d3e8cc8 100644 --- a/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java +++ b/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java @@ -13,10 +13,11 @@ import org.springframework.util.StringUtils; import rocks.inspectit.oce.eum.server.configuration.model.EumServerConfiguration; import rocks.inspectit.ocelot.config.model.exporters.ExportersSettings; -import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerExporterSettings; +import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerGrpcExporterSettings; import rocks.inspectit.ocelot.config.model.exporters.trace.TraceExportersSettings; import javax.annotation.PostConstruct; +import javax.validation.Valid; import java.util.Optional; @Configuration @@ -30,8 +31,8 @@ public class TraceExportersConfiguration { public void logWrongJaegerConfig() { Optional.ofNullable(configuration.getExporters()) .map(ExportersSettings::getTracing) - .map(TraceExportersSettings::getJaeger) - .filter(JaegerExporterSettings::isEnabled) + .map(TraceExportersSettings::getJaegerGrpc) + .filter(JaegerGrpcExporterSettings::isEnabled) .ifPresent(settings -> { if (!StringUtils.isEmpty(settings.getUrl()) && StringUtils.isEmpty(settings.getGrpc())) { log.warn("In order to use Jaeger span exporter, please specify the grpc API endpoint property instead of the url."); @@ -40,23 +41,22 @@ public void logWrongJaegerConfig() { } @Bean(destroyMethod = "shutdown") - @ConditionalOnProperty({"inspectit-eum-server.exporters.tracing.jaeger.enabled", "inspectit-eum-server.exporters.tracing.jaeger.grpc"}) - @ConditionalOnExpression("new String('${inspectit-eum-server.exporters.tracing.jaeger.grpc}').length() > 0") + @ConditionalOnProperty({"inspectit-eum-server.exporters.tracing.jaegerGrpc.enabled", "inspectit-eum-server.exporters.tracing.jaegerGrpc.grpc"}) + @ConditionalOnExpression("new String('${inspectit-eum-server.exporters.tracing.jaegerGrpc.grpc}').length() > 0") public SpanExporter jaegerSpanExporter() { - JaegerExporterSettings jaegerExporterSettings = configuration.getExporters().getTracing().getJaeger(); + @Valid JaegerGrpcExporterSettings jaegerExporterSettings = configuration.getExporters() + .getTracing() + .getJaegerGrpc(); - ManagedChannel channel = ManagedChannelBuilder.forTarget(jaegerExporterSettings.getGrpc()).usePlaintext().build(); - if(!StringUtils.isEmpty(jaegerExporterSettings.getUrl()) && StringUtils.isEmpty(jaegerExporterSettings.getGrpc())) { - log.info("Starting Jaeger Exporter on url '{}'", jaegerExporterSettings.getUrl()); - } else { - log.info("Starting Jaeger Exporter on grpc '{}'", jaegerExporterSettings.getGrpc()); - } + ManagedChannel channel = ManagedChannelBuilder.forTarget(jaegerExporterSettings.getGrpc()) + .usePlaintext() + .build(); + + log.info("Starting Jaeger Exporter on grpc '{}'", jaegerExporterSettings.getGrpc()); System.setProperty("otel.resource.attributes", "service.name=" + jaegerExporterSettings.getServiceName()); - return JaegerGrpcSpanExporter.builder() - .setChannel(channel) - .build(); + return JaegerGrpcSpanExporter.builder().setChannel(channel).build(); } } diff --git a/components/inspectit-ocelot-eum-server/src/test/java/rocks/inspectit/oce/eum/server/exporters/JaegerExporterIntTest.java b/components/inspectit-ocelot-eum-server/src/test/java/rocks/inspectit/oce/eum/server/exporters/JaegerExporterIntTest.java index fb82ed5fbe..01b4eef238 100644 --- a/components/inspectit-ocelot-eum-server/src/test/java/rocks/inspectit/oce/eum/server/exporters/JaegerExporterIntTest.java +++ b/components/inspectit-ocelot-eum-server/src/test/java/rocks/inspectit/oce/eum/server/exporters/JaegerExporterIntTest.java @@ -49,8 +49,9 @@ static class EnvInitializer implements ApplicationContextInitializer LOGGER.log(Level.INFO, outputFrame.getUtf8String().replace("\n", ""))) // expose all relevant ports - .withExposedPorts(COLLECTOR_OTLP_GRPC_PORT, COLLECTOR_OTLP_HTTP_PORT, COLLECTOR_HEALTH_CHECK_PORT, COLLECTOR_JAEGER_THRIFT_HTTP_PORT, COLLECTOR_JAEGER_THRIFT_BINARY_PORT, COLLECTOR_JAEGER_THRIFT_COMPACT_PORT, COLLECTOR_JAEGER_GRPC_PORT, COLLECTOR_PROMETHEUS_PORT) + .withExposedPorts(COLLECTOR_OTLP_GRPC_PORT, COLLECTOR_OTLP_HTTP_PORT, COLLECTOR_HEALTH_CHECK_PORT, COLLECTOR_JAEGER_THRIFT_HTTP_PORT, COLLECTOR_JAEGER_THRIFT_BINARY_PORT, COLLECTOR_JAEGER_THRIFT_COMPACT_PORT, COLLECTOR_JAEGER_GRPC_PORT, COLLECTOR_PROMETHEUS_PORT, COLLECTOR_INFLUX_DB1_PORT, COLLECTOR_ZIPKIN_PORT) .waitingFor(Wait.forHttp("/").forPort(COLLECTOR_HEALTH_CHECK_PORT)); // collector.withStartupTimeout(Duration.of(1, ChronoUnit.MINUTES)); @@ -145,12 +159,15 @@ static String getEndpoint(Integer originalPort, String path) { /** * Creates a nested trace with parent and child span and flushes them. + * + * @param parentSpanName the name of the parent {@link Span} + * @param childSpanName the name of the child {@link Span} */ - void makeSpansAndFlush() { + void makeSpansAndFlush(String parentSpanName, String childSpanName) { // start span and nested span - Span parentSpan = getTracer().spanBuilder("openTelemetryParentSpan").startSpan(); + Span parentSpan = getTracer().spanBuilder(parentSpanName).startSpan(); try (Scope scope = parentSpan.makeCurrent()) { - Span childSpan = getTracer().spanBuilder("openTelemetryChildSpan").startSpan(); + Span childSpan = getTracer().spanBuilder(childSpanName).startSpan(); try (Scope child = childSpan.makeCurrent()) { // do sth } finally { @@ -164,6 +181,99 @@ void makeSpansAndFlush() { Instances.openTelemetryController.flush(); } + /** + * Records some dummy metrics and flushes them. + */ + void recordMetricsAndFlush() { + recordMetricsAndFlush(1, "my-key", "my-val"); + } + + /** + * Records a counter with the given value and tag + * + * @param value the value to add to the counter + * @param tagKey the key of the tag + * @param tagVal the value of the tag + */ + void recordMetricsAndFlush(int value, String tagKey, String tagVal) { + // get the meter and create a counter + Meter meter = GlobalOpenTelemetry.getMeterProvider() + .meterBuilder("rocks.inspectit.ocelot") + .setInstrumentationVersion("0.0.1") + .build(); + LongCounter counter = meter.counterBuilder("my-counter").setDescription("My counter").setUnit("1").build(); + + // record counter + counter.add(value, Attributes.of(AttributeKey.stringKey(tagKey), tagVal)); + + Instances.openTelemetryController.flush(); + + } + + /** + * Verifies that the metric with the given value and key/value attribute (tag) has been exported to and received by the {@link #grpcServer} + * + * @param value + * @param tagKey + * @param tagVal + */ + void awaitMetricsExported(int value, String tagKey, String tagVal) { + // create the attribute that we will use to verify that the metric has been written + KeyValue attribute = KeyValue.newBuilder() + .setKey(tagKey) + .setValue(AnyValue.newBuilder().setStringValue(tagVal).build()) + .build(); + + await().atMost(30, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(grpcServer.metricRequests.stream()).anyMatch(mReq -> mReq.getResourceMetricsList() + .stream() + .anyMatch(rm -> + // check for the "my-counter" metrics + rm.getInstrumentationLibraryMetrics(0).getMetrics(0).getName().equals("my-counter") + // check for the specific attribute and value + && rm.getInstrumentationLibraryMetrics(0) + .getMetricsList() + .stream() + .anyMatch(metric -> metric.getSum() + .getDataPointsList() + .stream() + .anyMatch(d -> d.getAttributesList() + .contains(attribute) && d.getAsInt() == value))))); + } + + /** + * Waits for the spans to be exported to and received by the {@link #grpcServer}. This method asserts that Spans with the given names exist and that the child's {@link io.opentelemetry.proto.trace.v1.Span#getParentSpanId()} equals the parent's {@link io.opentelemetry.proto.trace.v1.Span#getSpanId()} + * + * @param parentSpanName the name of the parent span + * @param childSpanName the name of the child span + */ + void awaitSpansExported(String parentSpanName, String childSpanName) { + + await().atMost(15, TimeUnit.SECONDS).untilAsserted(() -> { + + // get a flat list of spans + Stream> spansLis = grpcServer.traceRequests.stream() + .flatMap(tr -> tr.getResourceSpansList() + .stream() + .flatMap(rs -> rs.getInstrumentationLibrarySpansList() + .stream() + .map(ils -> ils.getSpansList()))); + + // assert that parent and child span are present and that the parent's spanId equals the child's parentSpanId + assertThat(spansLis.anyMatch(s -> s.stream() + .filter(span -> span.getName().equals(childSpanName)) + .findFirst() + .orElse(io.opentelemetry.proto.trace.v1.Span.getDefaultInstance()) + .getParentSpanId() + .equals(s.stream() + .filter(span -> span.getName().equals(parentSpanName)) + .findFirst() + .orElse(io.opentelemetry.proto.trace.v1.Span.getDefaultInstance()) + .getSpanId()))).isTrue(); + }); + + } + /** * OpenTelemetry Protocol gRPC Server */ diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java index 7e2e9d28a2..70a40459e9 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java @@ -27,7 +27,7 @@ void verifyTraceSent() { .pollInterval(1, TimeUnit.SECONDS) .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); - makeSpansAndFlush(); + makeSpansAndFlush("jaeger-grpc-parent","jaeger-grpc-child"); await().atMost(15, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java index 738499344b..e39707f1b2 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java @@ -30,7 +30,7 @@ void verifyTraceSent() { .pollInterval(1, TimeUnit.SECONDS) .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); - makeSpansAndFlush(); + makeSpansAndFlush("jaeger-thrift-parent","jaeger-thrift-child"); await().atMost(15, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java new file mode 100644 index 0000000000..893f846897 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java @@ -0,0 +1,45 @@ +package rocks.inspectit.ocelot.core.exporter; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import rocks.inspectit.ocelot.core.config.InspectitEnvironment; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class OtlpGrpcMetricsExporterServiceIntTest extends ExporterServiceIntegrationTestBase { + + public static final String OTLP_GRPC_METRICS_PATH = "/v1/metrics"; + + @Autowired + OtlpGrpcMetricsExporterService service; + + @Autowired + InspectitEnvironment environment; + + String tagKey = "otlp-grpc-metrics-test"; + + String tagVal = "random-val"; + + int metricVal = 1337; + + @Test + void verifyMetricsWritten() { + updateProperties(mps -> { + mps.setProperty("inspectit.exporters.metrics.otlp-grpc.url", getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_GRPC_METRICS_PATH)); + mps.setProperty("inspectit.exporters.metrics.otlp-grpc.export-interval", "500ms"); + mps.setProperty("inspectit.exporters.metrics.otlp-grpc.enabled", true); + }); + + await().atMost(5, TimeUnit.SECONDS) + .pollInterval(500, TimeUnit.MILLISECONDS) + .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + + recordMetricsAndFlush(metricVal, tagKey, tagVal); + + awaitMetricsExported(metricVal, tagKey, tagVal); + } + +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java index 99e13e493c..d69413f5bd 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java @@ -24,15 +24,12 @@ void verifyTraceSent() { properties.setProperty("inspectit.exporters.tracing.otlp-grpc.enabled", true); }); - System.out.println("endpoint: " + getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_GRPC_TRACING_PATH)); await().atMost(10, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); - makeSpansAndFlush(); + makeSpansAndFlush("otlp-grpc-parent", "otlp-grpc-child"); - await().atMost(15, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(grpcServer.traceRequests).hasSize(1)); + awaitSpansExported("otlp-grpc-parent", "otlp-grpc-child"); } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java new file mode 100644 index 0000000000..5019b4bb6c --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java @@ -0,0 +1,38 @@ +package rocks.inspectit.ocelot.core.exporter; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +/** + * Integration test for {@link ZipkinExporterService} using the {@link ExporterServiceIntegrationTestBase} + */ +public class ZipkinExporterServiceInt2Test extends ExporterServiceIntegrationTestBase { + + private static final String ZIPKIN_PATH = "/api/v2/spans"; + + @Autowired + ZipkinExporterService service; + + @Test + void verifyTraceSent() { + updateProperties(mps -> { + mps.setProperty("inspectit.exporters.tracing.zipkin.enabled", true); + mps.setProperty("inspectit.exporters.tracing.zipkin.url", getEndpoint(COLLECTOR_ZIPKIN_PORT, ZIPKIN_PATH)); + }); + + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + + makeSpansAndFlush("zipkin-parent", "zipkin-child"); + + awaitSpansExported("zipkin-parent", "zipkin-child"); + + } + +} + + diff --git a/inspectit-ocelot-core/src/test/resources/otel-config.yaml b/inspectit-ocelot-core/src/test/resources/otel-config.yaml index eb2fc09021..9234675e5e 100644 --- a/inspectit-ocelot-core/src/test/resources/otel-config.yaml +++ b/inspectit-ocelot-core/src/test/resources/otel-config.yaml @@ -16,7 +16,7 @@ receivers: grpc: http: - # accept Prometheus from port 8888 + # scrape Prometheus prometheus: config: scrape_configs: @@ -25,6 +25,12 @@ receivers: static_configs: - targets: [$PROMETHEUS_SCRAPE_TARGET] + # accept InfluxDB + influxdb: + + # accept Zipkin + zipkin: + exporters: logging: logLevel: $LOGGING_EXPORTER_LOG_lEVEL @@ -36,10 +42,10 @@ service: extensions: [health_check] pipelines: metrics: - receivers: [otlp, prometheus] + receivers: [otlp, prometheus, influxdb] exporters: [logging, otlp] traces: - receivers: [otlp, jaeger] + receivers: [otlp, jaeger, zipkin] exporters: [logging, otlp] logs: receivers: [otlp] From b55673b7774b2ef3fbe3f1ca0bfcb26135d9e572 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 14 Mar 2022 15:43:50 +0100 Subject: [PATCH 65/86] feat(open-telemetry): [#1297] remove unused ContextPropagators --- .../inspectit/ocelot/utils/TestUtils.java | 21 +++++++------------ .../OpenTelemetryControllerImpl.java | 1 - 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java index 311c523885..04e9fbbe7b 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java @@ -12,15 +12,9 @@ import io.opencensus.tags.TagValue; import io.opencensus.tags.Tags; import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; -import io.opentelemetry.context.propagation.ContextPropagators; -import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.exporter.logging.LoggingSpanExporter; -import io.opentelemetry.extension.trace.propagation.B3Propagator; -import io.opentelemetry.extension.trace.propagation.JaegerPropagator; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; @@ -31,6 +25,8 @@ import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import org.awaitility.Awaitility; import org.awaitility.core.ConditionTimeoutException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import rocks.inspectit.ocelot.bootstrap.AgentManager; import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.bootstrap.opentelemetry.NoopOpenTelemetryController; @@ -53,6 +49,8 @@ public class TestUtils { public static ConcurrentHashMap, Long> instrumentationTimeStamp = new ConcurrentHashMap<>(); + private static final Logger logger = LoggerFactory.getLogger(TestUtils.class); + static { Thread poller = new Thread(() -> { while (true) { @@ -328,7 +326,7 @@ public static TimeSeries getTimeseries(String metricName, Map ta public static InMemorySpanExporter initializeOpenTelemetryForSystemTesting() { if (NoopOpenTelemetryController.INSTANCE != Instances.openTelemetryController) { - System.out.println("shut down " + Instances.openTelemetryController.getClass().getSimpleName()); + logger.info("shut down " + Instances.openTelemetryController.getClass().getSimpleName()); // shut down any previously configured OTELs Instances.openTelemetryController.shutdown(); @@ -348,15 +346,13 @@ public static InMemorySpanExporter initializeOpenTelemetryForSystemTesting() { OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) - .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) .buildAndRegisterGlobal(); // set the OTEL_TRACER in OpenTelemetrySpanBuilderImpl via reflection. // this needs to be done in case that another code already registered OTEL and the OTEL_TRACER is pointing to the wrong Tracer try { - Field tracerField = null; Tracer tracer = GlobalOpenTelemetry.getTracer("io.opentelemetry.opencensusshim"); - tracerField = Class.forName("io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl") + Field tracerField = Class.forName("io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl") .getDeclaredField("OTEL_TRACER"); // set static final field tracerField.setAccessible(true); @@ -365,10 +361,9 @@ public static InMemorySpanExporter initializeOpenTelemetryForSystemTesting() { modifiers.setInt(tracerField, tracerField.getModifiers() & ~Modifier.FINAL); tracerField.set(null, tracer); - System.out.println("OTEL_TRACER updated to " + tracer + " (" + openTelemetry.getTracer("io.opentelemetry.opencensusshim") + ")"); + logger.info("OTEL_TRACER updated to {} ({})", tracer, openTelemetry.getTracer("io.opentelemetry.opencensusshim")); } catch (Exception e) { - e.printStackTrace(); - System.err.println("Failed to set OTEL_TRACER in OpenTelemetrySpanBuilderImpl"); + logger.error("Failed to set OTEL_TRACER in OpenTelemetrySpanBuilderImpl", e); } return inMemSpanExporter; diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index 7467f40c13..e48c3e4974 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -218,7 +218,6 @@ synchronized boolean configureOpenTelemetry() { OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setMeterProvider(sdkMeterProvider) - .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) .build(); // update OTEL openTelemetry.set(openTelemetrySdk, false, false); From eb7f10272750a70ca283ce2b1d30db5f5bd8dc40 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Wed, 16 Mar 2022 17:42:14 +0100 Subject: [PATCH 66/86] feat(open-telemetry): [#1297] update the `enabled` flag of metrics/trace exporters, update the related documentation --- .../LoggingMetricsExporterSettings.java | 3 +- .../OtlpGrpcMetricsExporterSettings.java | 3 +- .../trace/LoggingTraceExporterSettings.java | 3 +- .../trace/OtlpGrpcTraceExporterSettings.java | 3 +- .../ocelot/config/default/exporters.yml | 13 ++-- .../exporter/JaegerGrpcExporterService.java | 1 + .../LoggingMetricExporterService.java | 2 +- .../exporter/LoggingTraceExporterService.java | 2 +- .../OtlpGrpcMetricsExporterService.java | 12 +++- .../OtlpGrpcTraceExporterService.java | 10 ++- .../JaegerGrpcExporterServiceIntTest.java | 27 +++++++- ...aegerThriftHttpExporterServiceIntTest.java | 23 ++++++- .../LoggingMetricsExporterServiceIntTest.java | 11 +-- .../LoggingTraceExporterServiceIntTest.java | 17 ++--- ...OtlpGrpcMetricsExporterServiceIntTest.java | 22 +++++- .../OtlpGrpcTraceExporterServiceIntTest.java | 23 ++++++- .../ZipkinExporterServiceInt2Test.java | 26 ++++++- .../docs/metrics/metric-exporters.md | 36 +++++++--- .../docs/tracing/trace-exporters.md | 67 ++++++++++++++----- 19 files changed, 244 insertions(+), 60 deletions(-) diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/LoggingMetricsExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/LoggingMetricsExporterSettings.java index d862f874f9..041e9455b9 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/LoggingMetricsExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/LoggingMetricsExporterSettings.java @@ -3,6 +3,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.time.DurationMin; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import java.time.Duration; @@ -13,7 +14,7 @@ @NoArgsConstructor public class LoggingMetricsExporterSettings { - private boolean enabled; + private ExporterEnabledState enabled; /** * Defines how often metrics are pushed to the log. diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpGrpcMetricsExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpGrpcMetricsExporterSettings.java index 25b332cf5b..37431b0ac7 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpGrpcMetricsExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpGrpcMetricsExporterSettings.java @@ -3,6 +3,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.time.DurationMin; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import java.time.Duration; @@ -13,7 +14,7 @@ @NoArgsConstructor public class OtlpGrpcMetricsExporterSettings { - private boolean enabled; + private ExporterEnabledState enabled; /*** * The OTLP gRPC metrics endpoint to connect to. diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java index c59ce169d0..c2a6334f72 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java @@ -2,6 +2,7 @@ import lombok.Data; import lombok.NoArgsConstructor; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; /** * Settings for the {@link io.opentelemetry.exporter.logging.LoggingSpanExporter} @@ -10,7 +11,7 @@ @NoArgsConstructor public class LoggingTraceExporterSettings { - private boolean enabled; + private ExporterEnabledState enabled; private String serviceName; diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpGrpcTraceExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpGrpcTraceExporterSettings.java index 8f6f3cf711..62aac71552 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpGrpcTraceExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpGrpcTraceExporterSettings.java @@ -2,6 +2,7 @@ import lombok.Data; import lombok.NoArgsConstructor; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; /** * Settings for {@link rocks.inspectit.ocelot.core.exporter.OtlpGrpcTraceExporterService} @@ -10,7 +11,7 @@ @NoArgsConstructor public class OtlpGrpcTraceExporterSettings { - private boolean enabled; + private ExporterEnabledState enabled; /*** * The OTLP traces gRPC endpoint to connect to. diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml index 6322094401..7950d4f314 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml @@ -40,17 +40,17 @@ inspectit: # settings used in LoggingMetricsExporterService for the LoggingMetricExporter (https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/logging) logging: - enabled: false + enabled: DISABLED # the export interval of the metrics export-interval: ${inspectit.metrics.frequency} # settings for the OtlpGrpcMetricExporter used in OtlpGrpcMetricExporterService otlp-grpc: - enabled: false + enabled: IF_CONFIGURED # the export interval of the metrics export-interval: ${inspectit.metrics.frequency} - # the endpoint - url: http://127.0.0.1:4318 + # the gRPC API endpoint, e.g., http://127.0.0.1:4318 + url: null # settings for trace exporters tracing: @@ -78,9 +78,10 @@ inspectit: # settings used in LoggingTraceExporterService for the LoggingSpanExporter (https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/logging) logging: - enabled: false + enabled: DISABLED # settings for the OtlpGrpcSpanExporter used in OtlpGrpcTraceExporterService otlp-grpc: - enabled: false + enabled: IF_CONFIGURED + # the gRPC API endpoint, e.g., http://127.0.0.1:4318 url: null diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java index 57fb9722aa..7464a933fe 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java @@ -75,4 +75,5 @@ protected boolean doDisable() { } return true; } + } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java index 0def0986e6..d30ef83b77 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java @@ -43,7 +43,7 @@ protected void init() { @Override protected boolean checkEnabledForConfig(InspectitConfig configuration) { @Valid LoggingMetricsExporterSettings logging = configuration.getExporters().getMetrics().getLogging(); - return configuration.getMetrics().isEnabled() && logging.isEnabled(); + return configuration.getMetrics().isEnabled() && !logging.getEnabled().isDisabled(); } @Override diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java index a1084ccf4d..6b32ccee16 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java @@ -37,7 +37,7 @@ protected void init() { @Override protected boolean checkEnabledForConfig(InspectitConfig configuration) { @Valid LoggingTraceExporterSettings logging = configuration.getExporters().getTracing().getLogging(); - return configuration.getTracing().isEnabled() && logging.isEnabled(); + return configuration.getTracing().isEnabled() && !logging.getEnabled().isDisabled(); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterService.java index 1ece12a29b..3e20f80732 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterService.java @@ -5,9 +5,10 @@ import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import rocks.inspectit.ocelot.config.model.exporters.metrics.OtlpGrpcMetricsExporterSettings; import javax.validation.Valid; @@ -37,7 +38,14 @@ public OtlpGrpcMetricsExporterService() { @Override protected boolean checkEnabledForConfig(InspectitConfig configuration) { @Valid OtlpGrpcMetricsExporterSettings otlp = configuration.getExporters().getMetrics().getOtlpGrpc(); - return configuration.getMetrics().isEnabled() && !StringUtils.isEmpty(otlp.getUrl()) && otlp.isEnabled(); + if (configuration.getMetrics().isEnabled() && !otlp.getEnabled().isDisabled()) { + if (StringUtils.hasText(otlp.getUrl())) { + return true; + } else if (otlp.getEnabled().equals(ExporterEnabledState.ENABLED)) { + log.warn("OTLP gRPC Metric Exporter is enabled but 'url' is not set."); + } + } + return false; } @Override diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java index 27fe39b640..1efae319a7 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java @@ -7,6 +7,7 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import rocks.inspectit.ocelot.config.model.exporters.trace.OtlpGrpcTraceExporterSettings; import javax.validation.Valid; @@ -29,7 +30,14 @@ public OtlpGrpcTraceExporterService() { @Override protected boolean checkEnabledForConfig(InspectitConfig configuration) { @Valid OtlpGrpcTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlpGrpc(); - return configuration.getTracing().isEnabled() && !StringUtils.isEmpty(otlp.getUrl()) && otlp.isEnabled(); + if (configuration.getTracing().isEnabled() && !otlp.getEnabled().isDisabled()) { + if (org.springframework.util.StringUtils.hasText(otlp.getUrl())) { + return true; + } else if (otlp.getEnabled().equals(ExporterEnabledState.ENABLED)) { + log.warn("OTLP gRPC Trace Exporter is enabled but 'url' is not set."); + } + } + return false; } @Override diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java index f7acad66a3..a906b0dc18 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java @@ -1,6 +1,9 @@ package rocks.inspectit.ocelot.core.exporter; +import io.github.netmikey.logunit.api.LogCapturer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.LoggingEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; @@ -10,10 +13,17 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +/** + * Test for the {@link JaegerGrpcExporterService} + */ public class JaegerGrpcExporterServiceIntTest extends ExporterServiceIntegrationTestBase { public static String JAEGER_GRPC_PATH = "/v1/traces"; + @RegisterExtension + LogCapturer warnLogs = LogCapturer.create() + .captureForType(JaegerGrpcExporterService.class, org.slf4j.event.Level.WARN); + @Autowired JaegerGrpcExporterService service; @@ -49,6 +59,21 @@ void testDefault() { .getJaegerGrpc() .getEnabled()).isEqualTo(ExporterEnabledState.IF_CONFIGURED); // gRPC API endpoint should be null or empty - assertThat(environment.getCurrentConfig().getExporters().getTracing().getJaegerGrpc().getGrpc()).isNullOrEmpty(); + assertThat(environment.getCurrentConfig() + .getExporters() + .getTracing() + .getJaegerGrpc() + .getGrpc()).isNullOrEmpty(); } + + @DirtiesContext + @Test + void testNoUrlSet() { + updateProperties(props -> { + props.setProperty("inspectit.exporters.tracing.jaeger-grpc.grpc", ""); + props.setProperty("inspectit.exporters.tracing.jaeger-grpc.enabled", ExporterEnabledState.ENABLED); + }); + warnLogs.assertContains("'grpc'"); + } + } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java index e39707f1b2..70d7d8d103 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java @@ -1,14 +1,20 @@ package rocks.inspectit.ocelot.core.exporter; +import io.github.netmikey.logunit.api.LogCapturer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +/** + * Test for {@link JaegerExporterService} using the {@link ExporterServiceIntegrationTestBase} + */ @DirtiesContext public class JaegerThriftHttpExporterServiceIntTest extends ExporterServiceIntegrationTestBase { @@ -17,20 +23,23 @@ public class JaegerThriftHttpExporterServiceIntTest extends ExporterServiceInteg @Autowired JaegerExporterService service; + @RegisterExtension + LogCapturer warnLogs = LogCapturer.create().captureForType(JaegerExporterService.class, org.slf4j.event.Level.WARN); + @DirtiesContext @Test void verifyTraceSent() { // enable Jaeger Thrift exporter updateProperties(mps -> { - mps.setProperty("inspectit.exporters.tracing.jaeger.enabled", true); + mps.setProperty("inspectit.exporters.tracing.jaeger.enabled", ExporterEnabledState.ENABLED); mps.setProperty("inspectit.exporters.tracing.jaeger.url", getEndpoint(COLLECTOR_JAEGER_THRIFT_HTTP_PORT, JAEGER_PATH)); }); await().atMost(15, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); - makeSpansAndFlush("jaeger-thrift-parent","jaeger-thrift-child"); + makeSpansAndFlush("jaeger-thrift-parent", "jaeger-thrift-child"); await().atMost(15, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) @@ -38,4 +47,14 @@ void verifyTraceSent() { } + @DirtiesContext + @Test + void testNoUrlSet() { + updateProperties(props -> { + props.setProperty("inspectit.exporters.tracing.jaeger.url", ""); + props.setProperty("inspectit.exporters.tracing.jaeger.enabled", ExporterEnabledState.ENABLED); + }); + warnLogs.assertContains("'url'"); + } + } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java index 99587f5869..97464139e4 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java @@ -15,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.bootstrap.Instances; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import rocks.inspectit.ocelot.core.SpringTestBase; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; @@ -53,7 +54,7 @@ static void afterAll() { @BeforeEach void enableService() { - localSwitch(true); + localSwitch(ExporterEnabledState.ENABLED); Awaitility.await() .atMost(15, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) @@ -62,10 +63,10 @@ void enableService() { @AfterEach void disableService() { - localSwitch(false); + localSwitch(ExporterEnabledState.DISABLED); } - private void localSwitch(boolean enabled) { + private void localSwitch(ExporterEnabledState enabled) { updateProperties(props -> { props.setProperty("inspectit.exporters.metrics.logging.enabled", enabled); }); @@ -86,7 +87,7 @@ void testMasterSwitch() { @DirtiesContext @Test void testLocalSwitch() { - localSwitch(false); + localSwitch(ExporterEnabledState.DISABLED); assertThat(service.isEnabled()).isFalse(); } } @@ -155,7 +156,7 @@ void verifyOpenCensusMetricsWritten() throws InterruptedException { }); // now turn the exporter off and make sure that no more metrics are exported to the log - localSwitch(false); + localSwitch(ExporterEnabledState.DISABLED); // wait until everything is flushed Thread.sleep(500); int numEvents = metricLogs.getEvents().size(); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java index be6ba70f99..0ca8c33bd6 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java @@ -15,6 +15,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; import rocks.inspectit.ocelot.bootstrap.Instances; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import rocks.inspectit.ocelot.core.SpringTestBase; import java.util.concurrent.TimeUnit; @@ -57,11 +58,11 @@ static void afterAll() { @BeforeEach void enableService() { - localSwitch(true); + localSwitch(ExporterEnabledState.ENABLED); masterSwitch(true); } - private void localSwitch(boolean enabled) { + private void localSwitch(ExporterEnabledState enabled) { updateProperties(props -> { props.setProperty("inspectit.exporters.tracing.logging.enabled", enabled); }); @@ -87,7 +88,7 @@ void testMasterSwitch() { @Test void testLocalSwitch() { assertThat(service.isEnabled()).isTrue(); - localSwitch(false); + localSwitch(ExporterEnabledState.DISABLED); assertThat(service.isEnabled()).isFalse(); } } @@ -135,7 +136,7 @@ void verifyOpenTelemetryTraceSent() throws InterruptedException { int numEvents = spanLogs.size(); // turn off trace exporter - localSwitch(false); + localSwitch(ExporterEnabledState.DISABLED); // wait until the service is shut down Awaitility.waitAtMost(5, TimeUnit.SECONDS) @@ -147,7 +148,7 @@ void verifyOpenTelemetryTraceSent() throws InterruptedException { assertThat(spanLogs.size()).isEqualTo(numEvents); // turn the trace exporter on again - localSwitch(true); + localSwitch(ExporterEnabledState.ENABLED); Awaitility.waitAtMost(1, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); makeSpansAndFlush(); @@ -165,7 +166,7 @@ void testLoggingExporterDisabled() throws InterruptedException { assertThat(service.isEnabled()).isTrue(); // disable exporter service - localSwitch(false); + localSwitch(ExporterEnabledState.DISABLED); assertThat(service.isEnabled()).isFalse(); @@ -197,13 +198,13 @@ void verifyOpenCensusTraceSent() throws InterruptedException { int numEvents = spanLogs.size(); // turn off tracing exporter - localSwitch(false); + localSwitch(ExporterEnabledState.DISABLED); // make sure no more spans are recorded Awaitility.waitAtMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isFalse()); assertThat(spanLogs.size()).isEqualTo(numEvents); // turn tracing exporter back on - localSwitch(true); + localSwitch(ExporterEnabledState.ENABLED); Awaitility.waitAtMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); // make spans diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java index 893f846897..a484899f45 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java @@ -1,7 +1,11 @@ package rocks.inspectit.ocelot.core.exporter; +import io.github.netmikey.logunit.api.LogCapturer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import java.util.concurrent.TimeUnit; @@ -9,9 +13,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +/** + * Test class for {@link OtlpGrpcMetricsExporterService} + */ public class OtlpGrpcMetricsExporterServiceIntTest extends ExporterServiceIntegrationTestBase { public static final String OTLP_GRPC_METRICS_PATH = "/v1/metrics"; + @RegisterExtension + LogCapturer warnLogs = LogCapturer.create().captureForType(OtlpGrpcMetricsExporterService.class, org.slf4j.event.Level.WARN); @Autowired OtlpGrpcMetricsExporterService service; @@ -30,7 +39,7 @@ void verifyMetricsWritten() { updateProperties(mps -> { mps.setProperty("inspectit.exporters.metrics.otlp-grpc.url", getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_GRPC_METRICS_PATH)); mps.setProperty("inspectit.exporters.metrics.otlp-grpc.export-interval", "500ms"); - mps.setProperty("inspectit.exporters.metrics.otlp-grpc.enabled", true); + mps.setProperty("inspectit.exporters.metrics.otlp-grpc.enabled", ExporterEnabledState.ENABLED); }); await().atMost(5, TimeUnit.SECONDS) @@ -42,4 +51,15 @@ void verifyMetricsWritten() { awaitMetricsExported(metricVal, tagKey, tagVal); } + + @DirtiesContext + @Test + void testNoUrlSet() { + updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.otlp-grpc.url", ""); + props.setProperty("inspectit.exporters.metrics.otlp-grpc.enabled", ExporterEnabledState.ENABLED); + }); + warnLogs.assertContains("'url'"); + } + } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java index d69413f5bd..1bfa18a27a 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java @@ -1,27 +1,37 @@ package rocks.inspectit.ocelot.core.exporter; +import io.github.netmikey.logunit.api.LogCapturer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +/** + * Test for the {@link OtlpGrpcTraceExporterService} + */ @DirtiesContext public class OtlpGrpcTraceExporterServiceIntTest extends ExporterServiceIntegrationTestBase { public static final String OTLP_GRPC_TRACING_PATH = "/v1/trace"; + @RegisterExtension + LogCapturer warnLogs = LogCapturer.create().captureForType(OtlpGrpcTraceExporterService.class, org.slf4j.event.Level.WARN); + @Autowired private OtlpGrpcTraceExporterService service; + @DirtiesContext @Test void verifyTraceSent() { updateProperties(properties -> { properties.setProperty("inspectit.exporters.tracing.otlp-grpc.url", getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_GRPC_TRACING_PATH)); - properties.setProperty("inspectit.exporters.tracing.otlp-grpc.enabled", true); + properties.setProperty("inspectit.exporters.tracing.otlp-grpc.enabled", ExporterEnabledState.ENABLED); }); await().atMost(10, TimeUnit.SECONDS) @@ -32,4 +42,15 @@ void verifyTraceSent() { awaitSpansExported("otlp-grpc-parent", "otlp-grpc-child"); } + + @DirtiesContext + @Test + void testNoUrlSet() { + updateProperties(props -> { + props.setProperty("inspectit.exporters.tracing.otlp-grpc.url", ""); + props.setProperty("inspectit.exporters.tracing.otlp-grpc.enabled", ExporterEnabledState.ENABLED); + }); + warnLogs.assertContains("'url'"); + } + } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java index 5019b4bb6c..1f61e83fe2 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java @@ -1,7 +1,12 @@ package rocks.inspectit.ocelot.core.exporter; +import io.github.netmikey.logunit.api.LogCapturer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.LoggingEvent; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import java.util.concurrent.TimeUnit; @@ -15,13 +20,18 @@ public class ZipkinExporterServiceInt2Test extends ExporterServiceIntegrationTes private static final String ZIPKIN_PATH = "/api/v2/spans"; + @RegisterExtension + LogCapturer warnLogs = LogCapturer.create() + .captureForType(ZipkinExporterService.class, org.slf4j.event.Level.WARN); + @Autowired ZipkinExporterService service; + @DirtiesContext @Test void verifyTraceSent() { updateProperties(mps -> { - mps.setProperty("inspectit.exporters.tracing.zipkin.enabled", true); + mps.setProperty("inspectit.exporters.tracing.zipkin.enabled", ExporterEnabledState.ENABLED); mps.setProperty("inspectit.exporters.tracing.zipkin.url", getEndpoint(COLLECTOR_ZIPKIN_PORT, ZIPKIN_PATH)); }); @@ -33,6 +43,20 @@ void verifyTraceSent() { } + @DirtiesContext + @Test + void testNoUrlSet() { + updateProperties(props -> { + props.setProperty("inspectit.exporters.tracing.zipkin.url", ""); + props.setProperty("inspectit.exporters.tracing.zipkin.enabled", ExporterEnabledState.ENABLED); + }); + System.out.println("----- WARNING LOGS ----"); + for (LoggingEvent event : warnLogs.getEvents()) { + System.out.println(event.getMessage()); + } + warnLogs.assertContains("'url'"); + } + } diff --git a/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md b/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md index 89b5de29b5..0d05fdded5 100644 --- a/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md +++ b/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md @@ -10,19 +10,21 @@ If an exporter supports run-time updates it means that it can be enabled/disable This way you can, for example, change the endpoint where exporter pushes the metrics without a need to restart the application. In order to use run-time updates, you must enable one of the [externalized configuration methods](configuration/external-configuration-sources) that support dynamic updates. -inspectIT Ocelot currently supports the following OpenTelemetry metrics exporters: +inspectIT Ocelot currently supports the following metrics exporters: -|Exporter |Supports run-time updates| Push / Pull |Enabled by default +|Exporter |Supports run-time updates| Push / Pull |Enabled by default| |---|---|---|---| -|[Logging Exporter](#logging-exporter)|Yes|Pull|No -|[Prometheus Exporter](#prometheus-exporter)|Yes|Pull|No -|[InfluxDB Exporter](#influxdb-exporter)|Yes|Push|Yes +|[Logging Exporter (Metrics)](#logging-exporter-metrics) [[Homepage](https://github.com/open-telemetry/opentelemetry-java/blob/main/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/LoggingMetricExporter.java)]|Yes|Push|No| +|[Prometheus Exporter](#prometheus-exporter)|Yes|Pull|No| +|[InfluxDB Exporter](#influxdb-exporter)|Yes|Push|No| +|[OTLP Exporter (Metrics)](#otlp-exporter-metrics) [[Homepage](https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/otlp/metrics)]|Yes|Push|No| ->**Important note**: Starting with version `2.X.X`, inspectIT Ocelot moved from OpenCensus to OpenTelemetry. As a result, the `OpenCensus Agent Exporter` is no longer supported. +>**Important note**: Starting with version `2.X.X`, inspectIT Ocelot moved from OpenCensus to OpenTelemetry. As a result, the `OpenCensus Agent Exporter` is no longer supported. -## Logging Exporter +## Logging Exporter (Metrics) -The Logging exporter exports the metrics to the console. By default, the exporter is enabled. The following properties are nested properties below the `inspectit.exporters.metrics.logging`: +The Logging exporter exports the metrics to the system log. By default, the exporter is disabled. +The following properties are nested properties below the `inspectit.exporters.metrics.logging`: |Property |Default| Description |---|---|---| @@ -47,8 +49,6 @@ The following properties are nested properties below the `inspectit.exporters.me > Don't forget to check [the official OpenTelemetry Prometheus exporter documentation](https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/prometheus). ## InfluxDB Exporter ->**Important**: the InfluxDB exporter is currently not working - If enabled, metrics are pushed at a specified interval directly to a given InfluxDB v1.x instance. To enable the InfluxDB Exporters, it is only required to specify the `url`. @@ -73,4 +73,18 @@ The following properties are nested properties below the `inspectit.exporters.me |`.create-database`| `true` | If enabled, the database defined by the `database` property is automatically created on startup with an `autogen` retention policy if it does not exist yet. |`.export-interval`| refers to `inspectit.metrics.frequency` |Defines how often metrics are pushed to the InfluxDB. |`.counters-as-differences`| `true` |Defines whether counters are exported using their absolute value or as the increase between exports -|`buffer-size`| `40` | In case the InfluxDB is not reachable, failed writes will be buffered and written on the next export. This value defines the maximum number of batches to buffer. \ No newline at end of file +|`buffer-size`| `40` | In case the InfluxDB is not reachable, failed writes will be buffered and written on the next export. This value defines the maximum number of batches to buffer. + +## OTLP Exporter (Metrics) + +The OpenTelemetry Protocol (OTLP) exporters export the metrics to the desired endpoint at a specified interval. +To enable the OTLP exporters, it is only required to specify the `url`. + +### OTLP gRPC Exporter + +The following properties are nested properties below the `inspectit.exporters.metrics.otlp-grpc` property: + +| Property | Default | Description | +| ---------- | ---------- | ------------------------------------------------------------ | +| `.enabled` | `DISABLED` | If `ENABLED` or `IF_CONFIGURED`, the inspectIT Ocelot agent will try to start the OTLP gRPC metrics exporter. | +| `.url` | `null` | The OTLP gRPC endpoint to connect to, e.g. `http://localhost:4317` | \ No newline at end of file diff --git a/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md b/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md index c244a14cea..37c2acf801 100644 --- a/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md +++ b/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md @@ -7,18 +7,26 @@ Tracing exporters are responsible for passing the recorded tracing data to a cor inspectIT Ocelot currently supports the following trace exporters: -* [Logging](#logging-exporter) [[Homepage](https://github.com/open-telemetry/opentelemetry-java/blob/main/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/LoggingSpanExporter.java)] +* [Logging (Traces)](#logging-exporter-traces) [[Homepage](https://github.com/open-telemetry/opentelemetry-java/blob/main/exporters/logging/src/main/java/io/opentelemetry/exporter/logging/LoggingSpanExporter.java)] * [Zipkin](#zipkin-exporter) [[Homepage](https://zipkin.io/)] * [Jaeger](#jaeger-exporter) [[Homepage](https://www.jaegertracing.io/)] +* [OTLP (Traces)](#otlp-exporter-traces) [[Homepage](https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/otlp/trace)] ->**Important note**: Starting with version `1.15.0`, inspectIT Ocelot moved from OpenCensus to OpenTelemetry. As a result, the `OpenCensus Agent Exporter` is no longer supported and has been removed. +>**Important note**: Starting with version `2.X.0`, inspectIT Ocelot moved from OpenCensus to OpenTelemetry. As a result, the `OpenCensus Agent Exporter` is no longer supported and has been removed. > Additionally, with OpenTelemetry, inspectIT Ocelot does not support the `service-name` property for individual exporter services anymore. Thus, we removed the `service-name` property from the Jaeger and Zipkin exporter. Please use the global `inspectit.service-name` property instead. -## Logging Exporter +## Logging Exporter (Traces) The Logging exporter exports traces to the system log. By default, the Logging exporter is disabled. -The Logging trace exporter has the following properties: -- `inspectit.exporters.tracing.logging.enabled`: enables/disables the Logging trace exporter. +The following properties are nested properties below the `inspectit.exporters.tracing.logging` property: + +| Property | Default | Description | +| ---------- | ---------- | ------------------------------------------------------------ | +| `.enabled` | `DISABLED` | If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Logging trace exporter. | + +To make inspectIT Ocelot write the spans to the system log, the following JVM property can be used: + +`-Dinspectit.exporters.tracing.logging.enabled=ENABLED` ## Zipkin Exporter @@ -28,11 +36,10 @@ By default, the Zipkin exporter is enabled but the URL needed for the exporter t The following properties are nested properties below the `inspectit.exporters.tracing.zipkin` property: -|Property |Default| Description +|Property |Default| Description| |---|---|---| -|`.enabled`|`IF_CONFIGURED`|If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Zipkin exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`. -|`.url`|`null`|v2 URL under which the ZipKin server can be accessed (e.g. http://127.0.0.1:9411/api/v2/spans). -|`.service-name`|refers to `inspectit.service-name`|The service-name which will be used to publish the spans. +|`.enabled`|`IF_CONFIGURED`|If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Zipkin exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`.| +|`.url`|`null`|v2 URL under which the ZipKin server can be accessed (e.g. http://127.0.0.1:9411/api/v2/spans).| To make inspectIT Ocelot push the spans to a Zipkin server running on the same machine as the agent, the following JVM property can be used: @@ -42,20 +49,50 @@ To make inspectIT Ocelot push the spans to a Zipkin server running on the same m ## Jaeger Exporter -The Jaeger exports works exactly the same way as the [Zipkin Exporter](#zipkin-exporter). +The Jaeger exports works exactly the same way as the [Zipkin Exporter](#zipkin-exporter). InspectIT Ocelot supports thrift and gRPC Jaeger exporter. -By default, the Jaeger exporter is enabled but the URL needed for the exporter to actually start is set to `null`. +By default, the Jaeger exporters are enabled but the URL/gRPC endpoint needed for the exporter to actually start is set to `null`. + +### Jaeger Thrift Exporter The following properties are nested properties below the `inspectit.exporters.tracing.jaeger` property: -|Property |Default| Description +|Property |Default| Description| |---|---|---| -|`.enabled`|`IF_CONFIGURED`|If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Jaeger exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`. -|`.url`|`null`|URL under which the Jaeger Thrift server can be accessed (e.g. http://127.0.0.1:14268/api/traces). -|`.service-name`|refers to `inspectit.service-name`|The service-name which will be used to publish the spans. +|`.enabled`|`IF_CONFIGURED`|If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Jaeger exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`.| +|`.url`|`null`|URL under which the Jaeger thrift server can be accessed (e.g. http://127.0.0.1:14268/api/traces).| To make inspectIT Ocelot push the spans to a Jaeger server running on the same machine as the agent, the following JVM property can be used: ``` -Dinspectit.exporters.tracing.jaeger.url=http://127.0.0.1:14268/api/traces ``` + +### Jaeger gRPC Exporter + +The following properties are nested properties below the `inspectit.exporters.tracing.jaeger-grpc` property: + +| Property | Default | Description | +| ---------- | --------------- | ------------------------------------------------------------ | +| `.enabled` | `IF_CONFIGURED` | If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Jaeger exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`. | +| `.grpc` | `null` | gRPC endpoint under which the Jaeger gRPC server can be accessed (e.g. http://127.0.0.1:14250/v1/traces). | + +To make inspectIT Ocelot push the spans to a Jaeger server running on the same machine as the agent, the following JVM property can be used: + +``` +-Dinspectit.exporters.tracing.jaeger-grpc.url=http://127.0.0.1:14250/v1/traces +``` + +## OTLP Exporter (Traces) + +The OpenTelemetry Protocol (OTLP) exporters export the Traces in OTLP to the desired endpoint at a specified interval. +By default, the OTLP exporters are enabled but the URL needed for the exporter to actually start is set to `null`. + +## OTLP gRPC Exporter + +The following properties are nested properties below the `inspectit.exporters.traces.otlp-grpc` property: + +| Property | Default | Description | +| ---------- | ---------- | ------------------------------------------------------------ | +| `.enabled` | `DISABLED` | If `ENABLED` or `IF_CONFIGURED`, the inspectIT Ocelot agent will try to start the OTLP gRPC trace exporter. | +| `.url` | `null` | The OTLP gRPC endpoint to connect to, e.g. `http://localhost:4317` | From f9e4467a467b659c3e640122d45087a8ea3f932c Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Wed, 16 Mar 2022 17:56:10 +0100 Subject: [PATCH 67/86] feat(open-temeletry): [#1297] add `SLF4JBridgeHandlerUtils` to (un-)install the SLF4J bridge handler --- .../ocelot/core/SLF4JBridgeHandlerUtils.java | 30 +++++++++++++++++++ .../LoggingMetricsExporterServiceIntTest.java | 13 ++------ .../LoggingTraceExporterServiceIntTest.java | 13 ++------ ...OtlpGrpcMetricsExporterServiceIntTest.java | 5 ++-- .../OpenTelemetryControllerImplTest.java | 13 ++------ 5 files changed, 42 insertions(+), 32 deletions(-) create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/SLF4JBridgeHandlerUtils.java diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/SLF4JBridgeHandlerUtils.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/SLF4JBridgeHandlerUtils.java new file mode 100644 index 0000000000..6245d2ec85 --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/SLF4JBridgeHandlerUtils.java @@ -0,0 +1,30 @@ +package rocks.inspectit.ocelot.core; + +import org.slf4j.bridge.SLF4JBridgeHandler; + +/** + * Utility class for {@link SLF4JBridgeHandler} + */ +public class SLF4JBridgeHandlerUtils { + + /** + * Installs the {@link SLF4JBridgeHandler} to send java.util.logging to SLF4J + */ + public static void installSLF4JBridgeHandler() { + // enable jul -> slf4j bridge + // this is necessary as OTEL logs to jul, but we use the LogCapturer with logback + if (!SLF4JBridgeHandler.isInstalled()) { + SLF4JBridgeHandler.removeHandlersForRootLogger(); + SLF4JBridgeHandler.install(); + } + } + + /** + * Uninstalls the {@link SLF4JBridgeHandler} if installed. + */ + public static void uninstallSLF4jBridgeHandler() { + if (SLF4JBridgeHandler.isInstalled()) { + SLF4JBridgeHandler.uninstall(); + } + } +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java index 97464139e4..0bae0f204e 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java @@ -11,11 +11,11 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.RegisterExtension; -import org.slf4j.bridge.SLF4JBridgeHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.core.SLF4JBridgeHandlerUtils; import rocks.inspectit.ocelot.core.SpringTestBase; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; @@ -37,19 +37,12 @@ public class LoggingMetricsExporterServiceIntTest extends SpringTestBase { @BeforeAll static void beforeAll() { - // enable jul -> slf4j bridge - // this is necessary as OTEL logs to jul, but we use the LogCapturer with logback - if (!SLF4JBridgeHandler.isInstalled()) { - SLF4JBridgeHandler.removeHandlersForRootLogger(); - SLF4JBridgeHandler.install(); - } + SLF4JBridgeHandlerUtils.installSLF4JBridgeHandler(); } @AfterAll static void afterAll() { - if (SLF4JBridgeHandler.isInstalled()) { - SLF4JBridgeHandler.uninstall(); - } + SLF4JBridgeHandlerUtils.uninstallSLF4jBridgeHandler(); } @BeforeEach diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java index 0ca8c33bd6..1c0fa9078a 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterServiceIntTest.java @@ -10,12 +10,12 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.RegisterExtension; -import org.slf4j.bridge.SLF4JBridgeHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.core.SLF4JBridgeHandlerUtils; import rocks.inspectit.ocelot.core.SpringTestBase; import java.util.concurrent.TimeUnit; @@ -41,19 +41,12 @@ public class LoggingTraceExporterServiceIntTest extends SpringTestBase { @BeforeAll static void beforeAll() { - // enable jul -> slf4j bridge - // this is necessary as OTEL logs to jul, but we use the LogCapturer with logback - if (!SLF4JBridgeHandler.isInstalled()) { - SLF4JBridgeHandler.removeHandlersForRootLogger(); - SLF4JBridgeHandler.install(); - } + SLF4JBridgeHandlerUtils.installSLF4JBridgeHandler(); } @AfterAll static void afterAll() { - if (SLF4JBridgeHandler.isInstalled()) { - SLF4JBridgeHandler.uninstall(); - } + SLF4JBridgeHandlerUtils.uninstallSLF4jBridgeHandler(); } @BeforeEach diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java index a484899f45..659dff2b35 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java @@ -19,8 +19,10 @@ public class OtlpGrpcMetricsExporterServiceIntTest extends ExporterServiceIntegrationTestBase { public static final String OTLP_GRPC_METRICS_PATH = "/v1/metrics"; + @RegisterExtension - LogCapturer warnLogs = LogCapturer.create().captureForType(OtlpGrpcMetricsExporterService.class, org.slf4j.event.Level.WARN); + LogCapturer warnLogs = LogCapturer.create() + .captureForType(OtlpGrpcMetricsExporterService.class, org.slf4j.event.Level.WARN); @Autowired OtlpGrpcMetricsExporterService service; @@ -51,7 +53,6 @@ void verifyMetricsWritten() { awaitMetricsExported(metricVal, tagKey, tagVal); } - @DirtiesContext @Test void testNoUrlSet() { diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java index fac4fc1383..d233e9e0dc 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java @@ -31,11 +31,11 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.bridge.SLF4JBridgeHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.core.SLF4JBridgeHandlerUtils; import rocks.inspectit.ocelot.core.SpringTestBase; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService; @@ -349,19 +349,12 @@ static class ChangeMetrics extends SpringTestBase { @BeforeAll static void beforeAll() { - // enable jul -> slf4j bridge - // this is necessary as OTEL logs to jul, but we use the LogCapturer with logback - if (!SLF4JBridgeHandler.isInstalled()) { - SLF4JBridgeHandler.removeHandlersForRootLogger(); - SLF4JBridgeHandler.install(); - } + SLF4JBridgeHandlerUtils.installSLF4JBridgeHandler(); } @AfterAll static void afterAll() { - if (SLF4JBridgeHandler.isInstalled()) { - SLF4JBridgeHandler.uninstall(); - } + SLF4JBridgeHandlerUtils.uninstallSLF4jBridgeHandler(); } @BeforeAll From 2663b9aa4d027a89e6c2f79d6439c4e20607b77e Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Wed, 16 Mar 2022 18:10:54 +0100 Subject: [PATCH 68/86] feat(open-telemetry): [#1297] add `defaultSettings` tests to OTLP and Jaeger gRPC exporter services --- .../JaegerGrpcExporterServiceIntTest.java | 17 ++++++++++++++++- ...OtlpGrpcMetricsExporterServiceIntTest.java | 19 ++++++++++++++++++- .../OtlpGrpcTraceExporterServiceIntTest.java | 18 ++++++++++++++++++ .../ZipkinExporterServiceInt2Test.java | 4 ---- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java index a906b0dc18..407b8e5122 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java @@ -1,12 +1,13 @@ package rocks.inspectit.ocelot.core.exporter; import io.github.netmikey.logunit.api.LogCapturer; +import org.assertj.core.api.AssertionsForClassTypes; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import org.slf4j.event.LoggingEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerGrpcExporterSettings; import java.util.concurrent.TimeUnit; @@ -76,4 +77,18 @@ void testNoUrlSet() { warnLogs.assertContains("'grpc'"); } + @Test + void defaultSettings() { + // service is not running + AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); + + JaegerGrpcExporterSettings jaegerGrpc = environment.getCurrentConfig() + .getExporters() + .getTracing() + .getJaegerGrpc(); + // enabled property is set to IF_CONFIGURED + AssertionsForClassTypes.assertThat(jaegerGrpc.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); + // url is null or empty + AssertionsForClassTypes.assertThat(jaegerGrpc.getUrl()).isNullOrEmpty(); + } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java index 659dff2b35..a654c30c5a 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java @@ -1,11 +1,13 @@ package rocks.inspectit.ocelot.core.exporter; import io.github.netmikey.logunit.api.LogCapturer; +import org.assertj.core.api.AssertionsForClassTypes; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.metrics.OtlpGrpcMetricsExporterSettings; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import java.util.concurrent.TimeUnit; @@ -36,6 +38,7 @@ public class OtlpGrpcMetricsExporterServiceIntTest extends ExporterServiceIntegr int metricVal = 1337; + @DirtiesContext @Test void verifyMetricsWritten() { updateProperties(mps -> { @@ -62,5 +65,19 @@ void testNoUrlSet() { }); warnLogs.assertContains("'url'"); } - + + @Test + void defaultSettings() { + // service is not running + AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); + + OtlpGrpcMetricsExporterSettings otlpGrpc = environment.getCurrentConfig() + .getExporters() + .getMetrics() + .getOtlpGrpc(); + // enabled property is set to IF_CONFIGURED + AssertionsForClassTypes.assertThat(otlpGrpc.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); + // url is null or empty + AssertionsForClassTypes.assertThat(otlpGrpc.getUrl()).isNullOrEmpty(); + } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java index 1bfa18a27a..a7eeca5ec6 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java @@ -1,11 +1,14 @@ package rocks.inspectit.ocelot.core.exporter; import io.github.netmikey.logunit.api.LogCapturer; +import org.assertj.core.api.AssertionsForClassTypes; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.metrics.OtlpGrpcMetricsExporterSettings; +import rocks.inspectit.ocelot.config.model.exporters.trace.OtlpGrpcTraceExporterSettings; import java.util.concurrent.TimeUnit; @@ -53,4 +56,19 @@ void testNoUrlSet() { warnLogs.assertContains("'url'"); } + @Test + void defaultSettings() { + // service is not running + AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); + + OtlpGrpcTraceExporterSettings otlpGrpc = environment.getCurrentConfig() + .getExporters() + .getTracing() + .getOtlpGrpc(); + // enabled property is set to IF_CONFIGURED + AssertionsForClassTypes.assertThat(otlpGrpc.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); + // url is null or empty + AssertionsForClassTypes.assertThat(otlpGrpc.getUrl()).isNullOrEmpty(); + } + } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java index 1f61e83fe2..5aa49b3855 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java @@ -50,10 +50,6 @@ void testNoUrlSet() { props.setProperty("inspectit.exporters.tracing.zipkin.url", ""); props.setProperty("inspectit.exporters.tracing.zipkin.enabled", ExporterEnabledState.ENABLED); }); - System.out.println("----- WARNING LOGS ----"); - for (LoggingEvent event : warnLogs.getEvents()) { - System.out.println(event.getMessage()); - } warnLogs.assertContains("'url'"); } From 54e30cffc6f75e618ed82d437b28c8ccaad61be2 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Wed, 16 Mar 2022 18:53:01 +0100 Subject: [PATCH 69/86] feat(open-telemetry): [#1297] remove unused `ContextPropagators` in `OpenTelemetryControllerImpl` --- .../core/opentelemetry/OpenTelemetryControllerImpl.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index e48c3e4974..c86bf8064e 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -2,13 +2,7 @@ import com.google.common.annotations.VisibleForTesting; import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; -import io.opentelemetry.context.propagation.ContextPropagators; -import io.opentelemetry.context.propagation.TextMapPropagator; -import io.opentelemetry.extension.trace.propagation.B3Propagator; -import io.opentelemetry.extension.trace.propagation.JaegerPropagator; import io.opentelemetry.opencensusshim.metrics.OpenCensusMetrics; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.SdkMeterProvider; @@ -345,7 +339,6 @@ void initOtel(InspectitConfig configuration) { OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) .setMeterProvider(meterProvider) - .setPropagators(ContextPropagators.create(TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance(), W3CBaggagePropagator.getInstance(), B3Propagator.injectingMultiHeaders()))) .build(); // build and register OpenTelemetryImpl From 731616a298d5bd13c2558ecad130fbdb1a4715ba Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 17 Mar 2022 09:26:23 +0100 Subject: [PATCH 70/86] feat(open-telemetry): [#1297] add integration test for OpenTelemetryControllerImpl --- .../OpenTelemetryControllerImpl.java | 2 +- .../OpenTelemetryControllerImplIntTest.java | 190 ++++++++++++++++++ .../OpenTelemetryControllerImplTest.java | 181 +---------------- 3 files changed, 195 insertions(+), 178 deletions(-) create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplIntTest.java diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index c86bf8064e..beca721c3f 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -396,7 +396,7 @@ synchronized SdkMeterProvider configureMeterProvider(InspectitConfig configurati if (null != meterProvider) { long start = System.nanoTime(); OpenTelemetryUtils.stopMeterProvider(meterProvider, true); - log.info("time to stopMeterProvider: {} ms", (System.nanoTime() - start) / 1000000); + // log.info("time to stopMeterProvider: {} ms", (System.nanoTime() - start) / 1000000); } // build new SdkMeterProvider SdkMeterProviderBuilder builder = SdkMeterProvider.builder().setResource(serviceNameResource); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplIntTest.java new file mode 100644 index 0000000000..ba1fee12cd --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplIntTest.java @@ -0,0 +1,190 @@ +package rocks.inspectit.ocelot.core.opentelemetry; + +import io.github.netmikey.logunit.api.LogCapturer; +import io.opencensus.trace.Tracing; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Scope; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.beans.factory.annotation.Autowired; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.core.SLF4JBridgeHandlerUtils; +import rocks.inspectit.ocelot.core.SpringTestBase; +import rocks.inspectit.ocelot.core.exporter.LoggingTraceExporterService; +import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +/** + * Integration test class for the {@link OpenTelemetryControllerImpl} + */ +public class OpenTelemetryControllerImplIntTest extends SpringTestBase { + + private static CloseableHttpClient testClient; + + @Autowired + OpenTelemetryControllerImpl openTelemetryController; + + @RegisterExtension + LogCapturer spanLogs = LogCapturer.create().captureForType(LoggingSpanExporter.class); + + @Autowired + LoggingTraceExporterService loggingTraceExporterService; + + @BeforeAll + static void beforeAll() { + SLF4JBridgeHandlerUtils.installSLF4JBridgeHandler(); + } + + @AfterAll + static void afterAll() { + SLF4JBridgeHandlerUtils.uninstallSLF4jBridgeHandler(); + } + + @BeforeAll + private static void initTestClient() { + RequestConfig.Builder requestBuilder = RequestConfig.custom(); + requestBuilder = requestBuilder.setConnectTimeout(1000); + requestBuilder = requestBuilder.setConnectionRequestTimeout(1000); + + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setDefaultRequestConfig(requestBuilder.build()); + testClient = builder.build(); + } + + @AfterAll + static void closeClient() throws Exception { + testClient.close(); + } + + void assertGet200(String url) throws Exception { + CloseableHttpResponse response = testClient.execute(new HttpGet(url)); + int statusCode = response.getStatusLine().getStatusCode(); + assertThat(statusCode).isEqualTo(200); + response.close(); + } + + void assertUnavailable(String url) { + Throwable throwable = catchThrowable(() -> testClient.execute(new HttpGet(url)) + .getStatusLine() + .getStatusCode()); + + assertThat(throwable).isInstanceOf(IOException.class); + } + + /** + * Test changes in MetricsExporterSettings, which will lead to {@link SdkMeterProvider} being rebuilt and re-registered to {@link OpenTelemetryImpl} + * + * @throws Exception + */ + @Test + void testChangeMetricsExporterServices() throws Exception { + + SdkMeterProvider sdkMeterProvider = openTelemetryController.getMeterProvider(); + // enable prometheus and logging + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.metrics.prometheus.enabled", ExporterEnabledState.ENABLED); + properties.setProperty("inspectit.exporters.metrics.logging.enabled", ExporterEnabledState.ENABLED); + }); + // wait until the OpenTelemetryController has been reconfigured + SdkMeterProvider newSdkMeterProvider = openTelemetryController.getMeterProvider(); + // meter provider should have changed + assertThat(sdkMeterProvider).isNotSameAs(newSdkMeterProvider); + // Prometheus should be running + assertGet200("http://localhost:8888/metrics"); + + // disable prometheus + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.metrics.prometheus.enabled", ExporterEnabledState.DISABLED); + }); + assertUnavailable("http://localhost:8888/metrics"); + + // wait until the SdkMeterProvider has been rebuilt + Awaitility.await() + .atMost(15, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(newSdkMeterProvider).isNotSameAs(openTelemetryController.getMeterProvider())); + + // enable prometheus + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.metrics.prometheus.enabled", ExporterEnabledState.ENABLED); + }); + assertGet200("http://localhost:8888/metrics"); + + } + + /** + * Verify that the {@link io.opencensus.trace.Tracer} in {@link Tracing#getTracer()} is correctly set to {@link GlobalOpenTelemetry#getTracerProvider()} + * + * @throws InterruptedException + */ + @Test + void testChangeTracingExporterServices() throws InterruptedException { + SdkTracerProvider sdkTracerProvider = openTelemetryController.getTracerProvider(); + // enable logging + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.tracing.logging.enabled", ExporterEnabledState.ENABLED); + }); + assertThat(loggingTraceExporterService.isEnabled()).isTrue(); + // make OC spans and flush + makeOCSpansAndFlush("test-span"); + // verify the spans are logged + Awaitility.waitAtMost(5, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(spanLogs.getEvents()).hasSize(1)); + assertThat(sdkTracerProvider).isEqualTo(openTelemetryController.getTracerProvider()); + + // shut off tracer + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.tracing.logging.enabled", ExporterEnabledState.DISABLED); + }); + assertThat(loggingTraceExporterService.isEnabled()).isFalse(); + // make OC spans and flush + makeOCSpansAndFlush("ignored-span"); + // verify that no more spans are logged + Thread.sleep(5000); + assertThat(spanLogs.getEvents()).hasSize(1); + } + + private static void makeOtelSpansAndFlush(String spanName) { + // build and flush span + Span span = GlobalOpenTelemetry.getTracerProvider() + .get("rocks.inspectit.instrumentation.test") + .spanBuilder(spanName) + .startSpan(); + try (Scope scope = span.makeCurrent()) { + } finally { + span.end(); + } + OpenTelemetryUtils.flush(); + } + + private static void makeOCSpansAndFlush(String spanName) { + // get OC tracer and start spans + io.opencensus.trace.Tracer tracer = Tracing.getTracer(); + + // start span + try (io.opencensus.common.Scope scope = tracer.spanBuilder(spanName).startScopedSpan()) { + io.opencensus.trace.Span span = tracer.getCurrentSpan(); + span.addAnnotation("anno"); + } + OpenTelemetryUtils.flush(); + } + +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java index d233e9e0dc..f77d502988 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java @@ -1,58 +1,39 @@ package rocks.inspectit.ocelot.core.opentelemetry; -import io.github.netmikey.logunit.api.LogCapturer; -import io.opencensus.trace.Tracing; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.TracerProvider; -import io.opentelemetry.context.Scope; import io.opentelemetry.exporter.logging.LoggingMetricExporter; -import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.export.SpanExporter; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.awaitility.Awaitility; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.config.model.InspectitConfig; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; -import rocks.inspectit.ocelot.core.SLF4JBridgeHandlerUtils; -import rocks.inspectit.ocelot.core.SpringTestBase; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableTraceExporterService; -import rocks.inspectit.ocelot.core.exporter.LoggingTraceExporterService; -import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; -import java.io.IOException; import java.lang.reflect.Method; -import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.Mockito.*; /** - * Test class for {@link OpenTelemetryControllerImpl} + * Test class for unit tests of {@link OpenTelemetryControllerImpl} */ @ExtendWith(MockitoExtension.class) class OpenTelemetryControllerImplTest { @@ -335,158 +316,4 @@ public String getName() { } } } - - /** - * Test changes in MetricsExporterSettings, which will lead to {@link SdkMeterProvider} being rebuilt and re-registered to {@link OpenTelemetryImpl} - */ - @Nested - static class ChangeMetrics extends SpringTestBase { - - @Autowired - OpenTelemetryControllerImpl openTelemetryController; - - private static CloseableHttpClient testClient; - - @BeforeAll - static void beforeAll() { - SLF4JBridgeHandlerUtils.installSLF4JBridgeHandler(); - } - - @AfterAll - static void afterAll() { - SLF4JBridgeHandlerUtils.uninstallSLF4jBridgeHandler(); - } - - @BeforeAll - private static void initTestClient() { - RequestConfig.Builder requestBuilder = RequestConfig.custom(); - requestBuilder = requestBuilder.setConnectTimeout(1000); - requestBuilder = requestBuilder.setConnectionRequestTimeout(1000); - - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setDefaultRequestConfig(requestBuilder.build()); - testClient = builder.build(); - } - - @AfterAll - static void closeClient() throws Exception { - testClient.close(); - } - - void assertGet200(String url) throws Exception { - CloseableHttpResponse response = testClient.execute(new HttpGet(url)); - int statusCode = response.getStatusLine().getStatusCode(); - assertThat(statusCode).isEqualTo(200); - response.close(); - } - - void assertUnavailable(String url) { - Throwable throwable = catchThrowable(() -> testClient.execute(new HttpGet(url)) - .getStatusLine() - .getStatusCode()); - - assertThat(throwable).isInstanceOf(IOException.class); - } - - @Test - void testChangeMetricsExporterServices() throws Exception { - - SdkMeterProvider sdkMeterProvider = openTelemetryController.getMeterProvider(); - // enable prometheus and logging - updateProperties(properties -> { - properties.setProperty("inspectit.exporters.metrics.prometheus.enabled", ExporterEnabledState.ENABLED); - properties.setProperty("inspectit.exporters.metrics.logging.enabled", true); - }); - // wait until the OpenTelemetryController has been reconfigured - SdkMeterProvider newSdkMeterProvider = openTelemetryController.getMeterProvider(); - // meter provider should have changed - assertThat(sdkMeterProvider).isNotSameAs(newSdkMeterProvider); - // Prometheus should be running - assertGet200("http://localhost:8888/metrics"); - - // disable prometheus - updateProperties(properties -> { - properties.setProperty("inspectit.exporters.metrics.prometheus.enabled", ExporterEnabledState.DISABLED); - }); - assertUnavailable("http://localhost:8888/metrics"); - - // wait until the SdkMeterProvider has been rebuilt - Awaitility.await() - .atMost(15, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(newSdkMeterProvider).isNotSameAs(openTelemetryController.getMeterProvider())); - - // enable prometheus - updateProperties(properties -> { - properties.setProperty("inspectit.exporters.metrics.prometheus.enabled", ExporterEnabledState.ENABLED); - }); - assertGet200("http://localhost:8888/metrics"); - - } - - @RegisterExtension - LogCapturer spanLogs = LogCapturer.create().captureForType(LoggingSpanExporter.class); - - @Autowired - LoggingTraceExporterService loggingTraceExporterService; - - /** - * Verify that the {@link io.opencensus.trace.Tracer} in {@link Tracing#getTracer()} is correctly set to {@link GlobalOpenTelemetry#getTracerProvider()} - * - * @throws InterruptedException - */ - @Test - void testChangeTracingExporterServices() throws InterruptedException { - SdkTracerProvider sdkTracerProvider = openTelemetryController.getTracerProvider(); - // enable logging - updateProperties(properties -> { - properties.setProperty("inspectit.exporters.tracing.logging.enabled", true); - }); - assertThat(loggingTraceExporterService.isEnabled()).isTrue(); - // make OC spans and flush - makeOCSpansAndFlush("test-span"); - // verify the spans are logged - Awaitility.waitAtMost(5, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(spanLogs.getEvents()).hasSize(1)); - assertThat(sdkTracerProvider).isEqualTo(openTelemetryController.getTracerProvider()); - - // shut off tracer - updateProperties(properties -> { - properties.setProperty("inspectit.exporters.tracing.logging.enabled", false); - }); - assertThat(loggingTraceExporterService.isEnabled()).isFalse(); - // make OC spans and flush - makeOCSpansAndFlush("ignored-span"); - // verify that no more spans are logged - Thread.sleep(5000); - assertThat(spanLogs.getEvents()).hasSize(1); - } - - private static void makeOtelSpansAndFlush(String spanName) { - // build and flush span - Span span = GlobalOpenTelemetry.getTracerProvider() - .get("rocks.inspectit.instrumentation.test") - .spanBuilder(spanName) - .startSpan(); - try (Scope scope = span.makeCurrent()) { - } finally { - span.end(); - } - OpenTelemetryUtils.flush(); - } - - private static void makeOCSpansAndFlush(String spanName) { - // get OC tracer and start spans - io.opencensus.trace.Tracer tracer = Tracing.getTracer(); - - // start span - try (io.opencensus.common.Scope scope = tracer.spanBuilder(spanName).startScopedSpan()) { - io.opencensus.trace.Span span = tracer.getCurrentSpan(); - span.addAnnotation("anno"); - } - OpenTelemetryUtils.flush(); - } - } - } \ No newline at end of file From ca6e3be989fce4d77505659ae1f052b1200dbb79 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Fri, 25 Mar 2022 15:27:32 +0100 Subject: [PATCH 71/86] feat(exporter): [#1297] rename `url` property of exporters to `endpoint`; adds `protocol` property to Jaeger and OTLP exporters and removes redundant exporters --- .../exporters/InfluxExporterService.java | 12 +- .../TraceExportersConfiguration.java | 20 +- .../exporters/JaegerExporterIntTest.java | 2 +- .../TraceExportersConfigurationTest.java | 10 +- .../InspectitConfigConversionService.java | 1 + .../StringToTransportProtocolConverter.java | 16 ++ .../model/exporters/TransportProtocol.java | 32 +++ .../metrics/InfluxExporterSettings.java | 8 +- .../metrics/MetricsExportersSettings.java | 2 +- .../OtlpGrpcMetricsExporterSettings.java | 30 --- .../metrics/OtlpMetricsExporterSettings.java | 43 ++++ .../trace/JaegerExporterSettings.java | 18 +- .../trace/JaegerGrpcExporterSettings.java | 29 --- .../trace/OtlpGrpcTraceExporterSettings.java | 22 -- .../trace/OtlpTraceExporterSettings.java | 35 +++ .../trace/TraceExportersSettings.java | 4 +- .../trace/ZipkinExporterSettings.java | 8 +- .../ocelot/config/default/exporters.yml | 32 ++- inspectit-ocelot-core/build.gradle | 3 +- .../core/exporter/InfluxExporterService.java | 15 +- .../core/exporter/JaegerExporterService.java | 40 +++- .../exporter/JaegerGrpcExporterService.java | 79 ------- .../OtlpGrpcMetricsExporterService.java | 89 -------- .../OtlpGrpcTraceExporterService.java | 76 ------- .../exporter/OtlpMetricsExporterService.java | 121 ++++++++++ .../exporter/OtlpTraceExporterService.java | 105 +++++++++ .../core/exporter/ZipkinExporterService.java | 14 +- .../InfluxExporterServiceIntTest.java | 11 +- .../JaegerExporterServiceIntTest.java | 208 ++++++++++++++---- .../JaegerGrpcExporterServiceIntTest.java | 94 -------- ...aegerThriftHttpExporterServiceIntTest.java | 60 ----- ...OtlpGrpcMetricsExporterServiceIntTest.java | 83 ------- .../OtlpGrpcTraceExporterServiceIntTest.java | 74 ------- .../OtlpMetricsExporterServiceIntTest.java | 121 ++++++++++ .../OtlpTraceExporterServiceIntTest.java | 107 +++++++++ .../ZipkinExporterServiceInt2Test.java | 21 +- .../ZipkinExporterServiceIntTest.java | 12 +- .../docs/breaking-changes/breaking-changes.md | 21 +- .../docs/metrics/metric-exporters.md | 39 ++-- .../docs/tracing/trace-exporters.md | 41 ++-- 40 files changed, 942 insertions(+), 816 deletions(-) create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/conversion/StringToTransportProtocolConverter.java create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java delete mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpGrpcMetricsExporterSettings.java create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpMetricsExporterSettings.java delete mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerGrpcExporterSettings.java delete mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpGrpcTraceExporterSettings.java create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterService.java delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterService.java create mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java delete mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java delete mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java delete mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java delete mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java diff --git a/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/InfluxExporterService.java b/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/InfluxExporterService.java index bf13e90799..89d31cc2a4 100644 --- a/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/InfluxExporterService.java +++ b/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/InfluxExporterService.java @@ -46,10 +46,13 @@ public class InfluxExporterService { private boolean shouldEnable() { InfluxExporterSettings influx = configuration.getExporters().getMetrics().getInflux(); if (!influx.getEnabled().isDisabled()) { - if (StringUtils.hasText(influx.getUrl())) { + if (StringUtils.hasText(influx.getEndpoint())) { + return true; + } else if (StringUtils.hasText(influx.getUrl())) { + log.warn("You are using the deprecated property 'url'. This property will be invalid in future releases of InspectIT Ocelot, please use `endpoint` instead."); return true; } else if (influx.getEnabled().equals(ExporterEnabledState.ENABLED)) { - log.warn("InfluxDB Exporter is enabled but no url set."); + log.warn("InfluxDB Exporter is enabled but 'endpoint' is not set."); } } return false; @@ -59,9 +62,10 @@ private boolean shouldEnable() { private void doEnable() { InfluxExporterSettings influx = configuration.getExporters().getMetrics().getInflux(); if (shouldEnable()) { - log.info("Starting InfluxDB Exporter to '{}:{}' on '{}'", influx.getDatabase(), influx.getRetentionPolicy(), influx.getUrl()); + String endpoint = StringUtils.hasText(influx.getEndpoint()) ? influx.getEndpoint() : influx.getUrl(); + log.info("Starting InfluxDB Exporter to '{}:{}' on '{}'", influx.getDatabase(), influx.getRetentionPolicy(), endpoint); activeExporter = InfluxExporter.builder() - .url(influx.getUrl()) + .url(endpoint) .database(influx.getDatabase()) .retention(influx.getRetentionPolicy()) .user(influx.getUser()) diff --git a/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java b/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java index a7a7fb6ad3..0329d90a76 100644 --- a/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java +++ b/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java @@ -13,7 +13,7 @@ import org.springframework.util.StringUtils; import rocks.inspectit.oce.eum.server.configuration.model.EumExportersSettings; import rocks.inspectit.oce.eum.server.configuration.model.EumServerConfiguration; -import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerGrpcExporterSettings; +import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerExporterSettings; import rocks.inspectit.ocelot.config.model.exporters.trace.TraceExportersSettings; import javax.annotation.PostConstruct; @@ -31,28 +31,26 @@ public class TraceExportersConfiguration { public void logWrongJaegerConfig() { Optional.ofNullable(configuration.getExporters()) .map(EumExportersSettings::getTracing) - .map(TraceExportersSettings::getJaegerGrpc) + .map(TraceExportersSettings::getJaeger) .filter((jaeger) -> !jaeger.getEnabled().isDisabled()) .ifPresent(settings -> { - if (StringUtils.hasText(settings.getUrl()) && !StringUtils.hasText(settings.getGrpc())) { - log.warn("In order to use Jaeger span exporter, please specify the grpc API endpoint property instead of the url."); + if (StringUtils.hasText(settings.getUrl()) && !StringUtils.hasText(settings.getEndpoint())) { + log.warn("In order to use Jaeger span exporter, please specify the grpc API 'endpoint' property instead of the 'url'."); } }); } @Bean(destroyMethod = "shutdown") - @ConditionalOnProperty({"inspectit-eum-server.exporters.tracing.jaegerGrpc.enabled", "inspectit-eum-server.exporters.tracing.jaegerGrpc.grpc"}) - @ConditionalOnExpression("(NOT new String('${inspectit-eum-server.exporters.tracing.jaegerGrpc.enabled}').toUpperCase().equals(T(rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState).DISABLED.toString())) AND (new String('${inspectit-eum-server.exporters.tracing.jaegerGrpc.grpc}').length() > 0)") + @ConditionalOnProperty({"inspectit-eum-server.exporters.tracing.jaeger.enabled", "inspectit-eum-server.exporters.tracing.jaeger.endpoint"}) + @ConditionalOnExpression("(NOT new String('${inspectit-eum-server.exporters.tracing.jaeger.enabled}').toUpperCase().equals(T(rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState).DISABLED.toString())) AND (new String('${inspectit-eum-server.exporters.tracing.jaeger.endpoint}').length() > 0)") public SpanExporter jaegerSpanExporter() { - @Valid JaegerGrpcExporterSettings jaegerExporterSettings = configuration.getExporters() - .getTracing() - .getJaegerGrpc(); + @Valid JaegerExporterSettings jaegerExporterSettings = configuration.getExporters().getTracing().getJaeger(); - ManagedChannel channel = ManagedChannelBuilder.forTarget(jaegerExporterSettings.getGrpc()) + ManagedChannel channel = ManagedChannelBuilder.forTarget(jaegerExporterSettings.getEndpoint()) .usePlaintext() .build(); - log.info("Starting Jaeger Exporter on grpc '{}'", jaegerExporterSettings.getGrpc()); + log.info("Starting Jaeger Exporter on grpc '{}'", jaegerExporterSettings.getEndpoint()); System.setProperty("otel.resource.attributes", "service.name=" + jaegerExporterSettings.getServiceName()); return JaegerGrpcSpanExporter.builder().setChannel(channel).build(); diff --git a/components/inspectit-ocelot-eum-server/src/test/java/rocks/inspectit/oce/eum/server/exporters/JaegerExporterIntTest.java b/components/inspectit-ocelot-eum-server/src/test/java/rocks/inspectit/oce/eum/server/exporters/JaegerExporterIntTest.java index f4ae9b2166..765902497a 100644 --- a/components/inspectit-ocelot-eum-server/src/test/java/rocks/inspectit/oce/eum/server/exporters/JaegerExporterIntTest.java +++ b/components/inspectit-ocelot-eum-server/src/test/java/rocks/inspectit/oce/eum/server/exporters/JaegerExporterIntTest.java @@ -49,7 +49,7 @@ static class EnvInitializer implements ApplicationContextInitializer { + + @Override + public TransportProtocol convert(String source) { + return StringUtils.hasText(source) ? TransportProtocol.parse(source) : TransportProtocol.UNSET; + } +} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java new file mode 100644 index 0000000000..b2ef23ccd2 --- /dev/null +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java @@ -0,0 +1,32 @@ +package rocks.inspectit.ocelot.config.model.exporters; + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; + +/** + * The transport protocols used by metrics and tracing exporters. + */ +public enum TransportProtocol { + UNKNOWN("unknown"), UNSET(""), GRPC("grpc"), HTTP_THRIFT("http/thrift"), HTTP_PROTOBUF("http/protobuf"), HTTP_JSON("http/json"), COUNT("cnt"); + + @Getter + private final String name; + + TransportProtocol(String name) { + this.name = name; + } + + static Map names = new HashMap<>(); + + static { + for (TransportProtocol tp : TransportProtocol.values()) { + names.put(tp.getName(), tp); + } + } + + public static TransportProtocol parse(String name) { + return names.containsKey(name) ? names.get(name) : TransportProtocol.valueOf(name); + } +} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/InfluxExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/InfluxExporterSettings.java index 0e0c627621..af9645334d 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/InfluxExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/InfluxExporterSettings.java @@ -22,10 +22,16 @@ public class InfluxExporterSettings { */ private ExporterEnabledState enabled; + @Deprecated /** + * This property is deprecated since v2.0. Please use {@link #endpoint} instead. * The HTTP URL of influx (e.g. http://localhost:8086) + */ private String url; + + /** + * The HTTP URL endpoint of Influx (e.g., http://localhost:8086) */ - private String url; + private String endpoint; /** * The database to which the values are pushed. diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java index 7de2395b3d..2165db0565 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/MetricsExportersSettings.java @@ -22,5 +22,5 @@ public class MetricsExportersSettings { private LoggingMetricsExporterSettings logging; @Valid - private OtlpGrpcMetricsExporterSettings otlpGrpc; + private OtlpMetricsExporterSettings otlp; } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpGrpcMetricsExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpGrpcMetricsExporterSettings.java deleted file mode 100644 index 37431b0ac7..0000000000 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpGrpcMetricsExporterSettings.java +++ /dev/null @@ -1,30 +0,0 @@ -package rocks.inspectit.ocelot.config.model.exporters.metrics; - -import lombok.Data; -import lombok.NoArgsConstructor; -import org.hibernate.validator.constraints.time.DurationMin; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; - -import java.time.Duration; - -/** - * Settings for {@link rocks.inspectit.ocelot.core.exporter.OtlpGrpcMetricsExporterService} - */ -@Data -@NoArgsConstructor -public class OtlpGrpcMetricsExporterSettings { - - private ExporterEnabledState enabled; - - /*** - * The OTLP gRPC metrics endpoint to connect to. - */ - private String url; - - /** - * Defines how often metrics are pushed to the log. - */ - @DurationMin(millis = 1) - private Duration exportInterval; - -} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpMetricsExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpMetricsExporterSettings.java new file mode 100644 index 0000000000..ef6b187944 --- /dev/null +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpMetricsExporterSettings.java @@ -0,0 +1,43 @@ +package rocks.inspectit.ocelot.config.model.exporters.metrics; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.time.DurationMin; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; + +import java.time.Duration; + +/** + * Settings for {@link rocks.inspectit.ocelot.core.exporter.OtlpMetricsExporterService} + */ +@Data +@NoArgsConstructor +public class OtlpMetricsExporterSettings { + + private ExporterEnabledState enabled; + + @Deprecated + /*** + * This property is deprecated since v2.0. Please use {@link #endpoint} instead. + * The OTLP metrics endpoint to connect to. + */ private String url; + + /** + * The OTLP metrics endpoint to connect to. + */ + private String endpoint; + + /** + * The transport protocol to use. + * Supported protocols are {@link TransportProtocol#GRPC} and {@link TransportProtocol#HTTP_PROTOBUF} + */ + private TransportProtocol protocol; + + /** + * Defines how often metrics are pushed to the log. + */ + @DurationMin(millis = 1) + private Duration exportInterval; + +} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java index 652d2a47b0..9e75ffaca0 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java @@ -3,6 +3,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; @Data @NoArgsConstructor @@ -13,10 +14,25 @@ public class JaegerExporterSettings { */ private ExporterEnabledState enabled; + @Deprecated /** + * This property is deprecated since v2.0. Please use {@link #endpoint} instead. * The URL of the Jaeger server. + */ private String url; + + /** + * The URL endpoint of the Jaeger server. */ - private String url; + private String endpoint; + /** + * The transport protocol to use. + * Supported protocols are {@link TransportProtocol#HTTP_THRIFT} and {@link TransportProtocol#GRPC} + */ + private TransportProtocol protocol; + /** + * The service name. Used in {@link rocks.inspectit.oce.eum.server.exporters.configuration.TraceExportersConfiguration} + */ + private String serviceName; } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerGrpcExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerGrpcExporterSettings.java deleted file mode 100644 index 8850ac3e52..0000000000 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerGrpcExporterSettings.java +++ /dev/null @@ -1,29 +0,0 @@ -package rocks.inspectit.ocelot.config.model.exporters.trace; - -import lombok.Data; -import lombok.NoArgsConstructor; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; - -@Data -@NoArgsConstructor -public class JaegerGrpcExporterSettings { - - private ExporterEnabledState enabled; - - /** - * The URL of the Jaeger server. This field is deprecated and only included for the PostConstruct test of {@link rocks.inspectit.oce.eum.server.exporters.configuration.TraceExportersConfiguration}. - */ - @Deprecated - private String url; - - /** - * The URI of the Jaeger gRPC proto-buf API. - */ - private String grpc; - - /** - * The service name. Used in {@link rocks.inspectit.oce.eum.server.exporters.configuration.TraceExportersConfiguration} - */ - private String serviceName; - -} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpGrpcTraceExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpGrpcTraceExporterSettings.java deleted file mode 100644 index 62aac71552..0000000000 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpGrpcTraceExporterSettings.java +++ /dev/null @@ -1,22 +0,0 @@ -package rocks.inspectit.ocelot.config.model.exporters.trace; - -import lombok.Data; -import lombok.NoArgsConstructor; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; - -/** - * Settings for {@link rocks.inspectit.ocelot.core.exporter.OtlpGrpcTraceExporterService} - */ -@Data -@NoArgsConstructor -public class OtlpGrpcTraceExporterSettings { - - private ExporterEnabledState enabled; - - /*** - * The OTLP traces gRPC endpoint to connect to. - */ - private String url; - - private String serviceName; -} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java new file mode 100644 index 0000000000..9ff9967f35 --- /dev/null +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java @@ -0,0 +1,35 @@ +package rocks.inspectit.ocelot.config.model.exporters.trace; + +import lombok.Data; +import lombok.NoArgsConstructor; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; + +/** + * Settings for {@link rocks.inspectit.ocelot.core.exporter.OtlpTraceExporterService} + */ +@Data +@NoArgsConstructor +public class OtlpTraceExporterSettings { + + private ExporterEnabledState enabled; + + @Deprecated + /*** + * This property is deprecated since v2.0. Please use {@link #endpoint} instead. + * The OTLP traces gRPC endpoint to connect to. + */ private String url; + + /** + * The OTLP traces endpoint to connect to. + */ + private String endpoint; + + /** + * The transport protocol to use. + * Supported protocols are {@link TransportProtocol#GRPC} and {@link TransportProtocol#HTTP_PROTOBUF} + */ + private TransportProtocol protocol; + + private String serviceName; +} diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java index 66ef0c7803..116d31e950 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/TraceExportersSettings.java @@ -12,8 +12,6 @@ public class TraceExportersSettings { @Valid private JaegerExporterSettings jaeger; - @Valid JaegerGrpcExporterSettings jaegerGrpc; - @Valid private ZipkinExporterSettings zipkin; @@ -21,6 +19,6 @@ public class TraceExportersSettings { private LoggingTraceExporterSettings logging; @Valid - private OtlpGrpcTraceExporterSettings otlpGrpc; + private OtlpTraceExporterSettings otlp; } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/ZipkinExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/ZipkinExporterSettings.java index 0bede882a3..837960d5cf 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/ZipkinExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/ZipkinExporterSettings.java @@ -13,9 +13,15 @@ public class ZipkinExporterSettings { */ private ExporterEnabledState enabled; + @Deprecated /** + * This property is deprecated since v2.0. Please use {@link #endpoint} instead. * The URL of the Zipkin server. + */ private String url; + + /** + * The URL endpoint of the Zipkin server. */ - private String url; + private String endpoint; } diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml index 7950d4f314..4497d9c7b2 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml @@ -20,7 +20,7 @@ inspectit: # Whether the exporter should be enabled, possible states are DISABLED, IF_CONFIGURED and ENABLED enabled: IF_CONFIGURED # the Url under which the InfluxDB can be accessed, e.g. http://localhost:8086 - url: null + endpoint: null # the export interval of the metrics export-interval: ${inspectit.metrics.frequency} # the database to write to @@ -45,12 +45,14 @@ inspectit: export-interval: ${inspectit.metrics.frequency} # settings for the OtlpGrpcMetricExporter used in OtlpGrpcMetricExporterService - otlp-grpc: + otlp: enabled: IF_CONFIGURED # the export interval of the metrics export-interval: ${inspectit.metrics.frequency} # the gRPC API endpoint, e.g., http://127.0.0.1:4318 - url: null + endpoint: null + # the transport protocol, e.g., grpc or http/protobuf + protocol: grpc # settings for trace exporters tracing: @@ -60,28 +62,24 @@ inspectit: # Whether the exporter should be enabled, possible states are DISABLED, IF_CONFIGURED and ENABLED enabled: IF_CONFIGURED # the v2 Url under which the ZipKin server can be accessed, e.g. http://127.0.0.1:9411/api/v2/spans - url: null - + endpoint: null # settings for the jaeger exporter (https://github.com/census-instrumentation/opencensus-java/tree/master/exporters/trace/jaeger) jaeger: # Whether the exporter should be enabled, possible states are DISABLED, IF_CONFIGURED and ENABLED enabled: IF_CONFIGURED - # the URL under which the jaeger thrift server can be accessed, e.g. http://127.0.0.1:14268/api/traces - url: null - # settings for Jaeger gRPC exporter - jaeger-grpc: - # Whether the exporter should be enabled, possible states are DISABLED, IF_CONFIGURED and ENABLED - enabled: IF_CONFIGURED - # the URL under which the Jaeger gRPC server can be accessed, e.g., http://127.0.0.1:14250/v1/traces - grpc: null + # the URL under which the jaeger thrift server can be accessed, e.g. http://127.0.0.1:14268/api/traces for http/thrift or http://127.0.0.1:14250/v1/traces for grpc + endpoint: null + # the transport protocol + protocol: http/thrift # settings used in LoggingTraceExporterService for the LoggingSpanExporter (https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/logging) logging: enabled: DISABLED - # settings for the OtlpGrpcSpanExporter used in OtlpGrpcTraceExporterService - otlp-grpc: + # settings for the OtlpGrpcSpanExporter/OtlpHttpSpanExporter used in OtlpTraceExporterService + otlp: enabled: IF_CONFIGURED - # the gRPC API endpoint, e.g., http://127.0.0.1:4318 - url: null + # the URL endpoint, e.g., http://127.0.0.1:4318 + endpoint: null + protocol: grpc diff --git a/inspectit-ocelot-core/build.gradle b/inspectit-ocelot-core/build.gradle index aafbf78ee4..ac9cca7f5f 100644 --- a/inspectit-ocelot-core/build.gradle +++ b/inspectit-ocelot-core/build.gradle @@ -83,8 +83,9 @@ dependencies { "io.opentelemetry:opentelemetry-exporter-jaeger-thrift", "io.opentelemetry:opentelemetry-exporter-zipkin", "io.opentelemetry:opentelemetry-exporter-otlp", + "io.opentelemetry:opentelemetry-exporter-otlp-http-trace", "io.opentelemetry:opentelemetry-exporter-otlp-metrics", - + "io.opentelemetry:opentelemetry-exporter-otlp-http-metrics", platform("io.opentelemetry:opentelemetry-bom-alpha:${openTelemetryAlphaVersion}"), "io.opentelemetry:opentelemetry-exporter-prometheus", diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterService.java index f016518912..e1a9ccde49 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterService.java @@ -58,12 +58,14 @@ public InfluxExporterService() { @Override protected boolean checkEnabledForConfig(InspectitConfig conf) { InfluxExporterSettings influx = conf.getExporters().getMetrics().getInflux(); - if ( conf.getMetrics() - .isEnabled() && !influx.getEnabled().isDisabled() ) { - if (StringUtils.hasText(influx.getUrl()) ) { + if (conf.getMetrics().isEnabled() && !influx.getEnabled().isDisabled()) { + if (StringUtils.hasText(influx.getEndpoint())) { + return true; + } else if (StringUtils.hasText(influx.getUrl())) { + log.warn("You are using the deprecated property 'url'. This property will be invalid in future releases of InspectIT Ocelot, please use 'endpoint' instead."); return true; } else if (influx.getEnabled().equals(ExporterEnabledState.ENABLED)) { - log.warn("InfluxDB Exporter is enabled but 'url' is not set."); + log.warn("InfluxDB Exporter is enabled but 'endpoint' is not set."); } } return false; @@ -75,15 +77,16 @@ protected boolean doEnable(InspectitConfig configuration) { String user = influx.getUser(); String password = influx.getPassword(); + String endpoint = StringUtils.hasText(influx.getEndpoint()) ? influx.getEndpoint() : influx.getUrl(); // check user and password, which are not allowed to be null as of v1.15.0 if (null == user) { user = DUMMY_USER; password = DUMMY_PASSWORD; LOGGER.warning(String.format("You are using the InfluxDB exporter without specifying 'user' and 'password'. Since v1.15.0, 'user' and 'password' are mandatory. Will be using the dummy user '%s' and dummy password '%s'.", DUMMY_USER, DUMMY_PASSWORD)); } - log.info("Starting InfluxDB Exporter to '{}:{}' on '{}'", influx.getDatabase(), influx.getRetentionPolicy(), influx.getUrl()); + log.info("Starting InfluxDB Exporter to '{}:{}' on '{}'", influx.getDatabase(), influx.getRetentionPolicy(), endpoint); activeExporter = InfluxExporter.builder() - .url(influx.getUrl()) + .url(endpoint) .database(influx.getDatabase()) .retention(influx.getRetentionPolicy()) .user(user) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java index 8e7287fe92..36dc4f5302 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java @@ -2,15 +2,19 @@ import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter; import io.opentelemetry.exporter.jaeger.thrift.JaegerThriftSpanExporter; +import io.opentelemetry.sdk.trace.export.SpanExporter; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerExporterSettings; import javax.validation.Valid; +import java.util.Arrays; +import java.util.List; /** * Service for the {@link JaegerThriftSpanExporter}. @@ -20,10 +24,10 @@ @Slf4j public class JaegerExporterService extends DynamicallyActivatableTraceExporterService { - private JaegerGrpcSpanExporter grpcSpanExporter; + private final List SUPPORTED_PROTOCOLS = Arrays.asList(TransportProtocol.GRPC, TransportProtocol.HTTP_THRIFT); @Getter - private JaegerThriftSpanExporter spanExporter; + private SpanExporter spanExporter; public JaegerExporterService() { super("exporters.tracing.jaeger", "tracing.enabled"); @@ -38,11 +42,23 @@ protected void init() { protected boolean checkEnabledForConfig(InspectitConfig conf) { @Valid JaegerExporterSettings jaeger = conf.getExporters().getTracing().getJaeger(); if (conf.getTracing().isEnabled() && !jaeger.getEnabled().isDisabled()) { - if (StringUtils.hasText(jaeger.getUrl())) { - return true; + if (SUPPORTED_PROTOCOLS.contains(jaeger.getProtocol())) { + if (StringUtils.hasText(jaeger.getEndpoint())) { + return true; + } else if (StringUtils.hasText(jaeger.getUrl())) { + log.warn("You are using the deprecated property 'url'. This property will be invalid in future releases of InspectIT Ocelot, please use 'endpoint' instead."); + return true; + } } if (jaeger.getEnabled().equals(ExporterEnabledState.ENABLED)) { - log.warn("Jaeger Exporter is enabled but 'url' is not set."); + if (!SUPPORTED_PROTOCOLS.contains(jaeger.getProtocol())) { + log.warn("Jaeger Exporter is enabled, but wrong 'protocol' is specified. Supported values are ", Arrays.toString(SUPPORTED_PROTOCOLS.stream() + .map(transportProtocol -> transportProtocol.getName()) + .toArray())); + } + if (!StringUtils.hasText(jaeger.getEndpoint()) && !StringUtils.hasText(jaeger.getUrl())) { + log.warn("Jaeger Exporter is enabled but 'endpoint' is not set."); + } } } return false; @@ -52,10 +68,20 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { protected boolean doEnable(InspectitConfig configuration) { try { JaegerExporterSettings settings = configuration.getExporters().getTracing().getJaeger(); - log.info("Starting Jaeger Thrift Exporter with url '{}'", settings.getUrl()); + String endpoint = StringUtils.hasText(settings.getEndpoint()) ? settings.getEndpoint() : settings.getUrl(); + log.info("Starting Jaeger Thrift Exporter with endpoint '{}'", endpoint); // create span exporter - spanExporter = JaegerThriftSpanExporter.builder().setEndpoint(settings.getUrl()).build(); + switch (settings.getProtocol()) { + case GRPC: { + spanExporter = JaegerGrpcSpanExporter.builder().setEndpoint(endpoint).build(); + break; + } + case HTTP_THRIFT: { + spanExporter = JaegerThriftSpanExporter.builder().setEndpoint(endpoint).build(); + break; + } + } // register openTelemetryController.registerTraceExporterService(this); diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java deleted file mode 100644 index 7464a933fe..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterService.java +++ /dev/null @@ -1,79 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import rocks.inspectit.ocelot.config.model.InspectitConfig; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; -import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerGrpcExporterSettings; - -import javax.validation.Valid; - -/** - * Service for the {@link JaegerGrpcSpanExporter} exporter. - * Can be dynamically started and stopped using the exporters.trace.jaeger-grpc.enabled configuration. - */ -@Component -@Slf4j -public class JaegerGrpcExporterService extends DynamicallyActivatableTraceExporterService { - - @Getter - private JaegerGrpcSpanExporter spanExporter; - - public JaegerGrpcExporterService() { - super("exporters.tracing.jaegerGrpc", "tracing.enabled"); - } - - @Override - protected boolean checkEnabledForConfig(InspectitConfig conf) { - @Valid JaegerGrpcExporterSettings jaeger = conf.getExporters().getTracing().getJaegerGrpc(); - if (conf.getTracing().isEnabled() && !jaeger.getEnabled().isDisabled()) { - - if (StringUtils.hasText(jaeger.getGrpc())) { - return true; - } else if (StringUtils.hasText(jaeger.getUrl())) { - // print warning if user used wrong setup - log.warn("In order to use Jaeger gRPC span exporter, please specify the 'grpc' API endpoint property instead of the 'url'."); - } - if (jaeger.getEnabled().equals(ExporterEnabledState.ENABLED)) { - log.warn("Jaeger Exporter is enabled but 'grpc' is not set."); - } - } - return false; - } - - @Override - protected boolean doEnable(InspectitConfig configuration) { - try { - JaegerGrpcExporterSettings settings = configuration.getExporters().getTracing().getJaegerGrpc(); - log.info("Starting Jaeger gRPC Exporter with grpc '{}'", settings.getGrpc()); - - spanExporter = JaegerGrpcSpanExporter.builder().setEndpoint(settings.getGrpc()).build(); - - // register - openTelemetryController.registerTraceExporterService(this); - - return true; - } catch (Throwable t) { - log.error("Error creating Jaeger gRPC Exporter", t); - return false; - } - } - - @Override - protected boolean doDisable() { - log.info("Stopping Jaeger gRPC Exporter"); - try { - openTelemetryController.unregisterTraceExporterService(this); - if (null != spanExporter) { - spanExporter.close(); - } - } catch (Throwable t) { - log.error("Error disabling Jaeger gRPC Exporter", t); - } - return true; - } - -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterService.java deleted file mode 100644 index 3e20f80732..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterService.java +++ /dev/null @@ -1,89 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; -import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import rocks.inspectit.ocelot.config.model.InspectitConfig; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; -import rocks.inspectit.ocelot.config.model.exporters.metrics.OtlpGrpcMetricsExporterSettings; - -import javax.validation.Valid; - -/** - * Service for {@link io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter}. - * Can be dynamically started and stopped using the exporters.metrics.otlp-grpc.enabled configuration - */ -@Component -@Slf4j -public class OtlpGrpcMetricsExporterService extends DynamicallyActivatableMetricsExporterService { - - /** - * The {@link OtlpGrpcMetricExporter} for exporting metrics via OTLP gRPC - */ - private OtlpGrpcMetricExporter metricExporter; - - /** - * The {@link PeriodicMetricReaderBuilder} for reading metrics to the log - */ - private PeriodicMetricReaderBuilder metricReaderBuilder; - - public OtlpGrpcMetricsExporterService() { - super("metrics.enabled", "exporters.metrics.otlpGrpc"); - } - - @Override - protected boolean checkEnabledForConfig(InspectitConfig configuration) { - @Valid OtlpGrpcMetricsExporterSettings otlp = configuration.getExporters().getMetrics().getOtlpGrpc(); - if (configuration.getMetrics().isEnabled() && !otlp.getEnabled().isDisabled()) { - if (StringUtils.hasText(otlp.getUrl())) { - return true; - } else if (otlp.getEnabled().equals(ExporterEnabledState.ENABLED)) { - log.warn("OTLP gRPC Metric Exporter is enabled but 'url' is not set."); - } - } - return false; - } - - @Override - protected boolean doEnable(InspectitConfig configuration) { - try { - // build and register exporter service - OtlpGrpcMetricsExporterSettings otlp = configuration.getExporters().getMetrics().getOtlpGrpc(); - metricExporter = OtlpGrpcMetricExporter.builder().setEndpoint(otlp.getUrl()).build(); - metricReaderBuilder = PeriodicMetricReader.builder(metricExporter).setInterval(otlp.getExportInterval()); - - boolean success = openTelemetryController.registerMetricExporterService(this); - if (success) { - log.info("Starting {}", getClass().getSimpleName()); - } else { - log.error("Failed to register {} at {}!", getClass().getSimpleName(), openTelemetryController.getClass() - .getSimpleName()); - } - return success; - } catch (Exception e) { - log.error("Error creatig OTLP metrics exporter service", e); - return false; - } - } - - @Override - protected boolean doDisable() { - try { - log.info("Stopping OtlpGrpcMetricsExporter"); - openTelemetryController.unregisterMetricExporterService(this); - return true; - } catch (Exception e) { - log.error("Failed to stop OtlpGrpcMetricsExporter", e); - return false; - } - } - - @Override - public MetricReaderFactory getNewMetricReaderFactory() { - return metricReaderBuilder.newMetricReaderFactory(); - } -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java deleted file mode 100644 index 1efae319a7..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterService.java +++ /dev/null @@ -1,76 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; -import rocks.inspectit.ocelot.config.model.InspectitConfig; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; -import rocks.inspectit.ocelot.config.model.exporters.trace.OtlpGrpcTraceExporterSettings; - -import javax.validation.Valid; - -/** - * Service for {@link io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter}. - * Can be dynamically started and stopped using the exporters.trace.otlp-grpc.enabled configuration - */ -@Component -@Slf4j -public class OtlpGrpcTraceExporterService extends DynamicallyActivatableTraceExporterService { - - @Getter - private SpanExporter spanExporter; - - public OtlpGrpcTraceExporterService() { - super("exporters.tracing.otlpGrpc", "tracing.enabled"); - } - - @Override - protected boolean checkEnabledForConfig(InspectitConfig configuration) { - @Valid OtlpGrpcTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlpGrpc(); - if (configuration.getTracing().isEnabled() && !otlp.getEnabled().isDisabled()) { - if (org.springframework.util.StringUtils.hasText(otlp.getUrl())) { - return true; - } else if (otlp.getEnabled().equals(ExporterEnabledState.ENABLED)) { - log.warn("OTLP gRPC Trace Exporter is enabled but 'url' is not set."); - } - } - return false; - } - - @Override - protected boolean doEnable(InspectitConfig configuration) { - try { - OtlpGrpcTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlpGrpc(); - log.info("Starting OTLP Trace Exporter with endpoint {}", otlp.getUrl()); - - // create span exporter - spanExporter = OtlpGrpcSpanExporter.builder().setEndpoint(otlp.getUrl()).build(); - - // register service - openTelemetryController.registerTraceExporterService(this); - return true; - } catch (Throwable t) { - log.error("Error creating OTLP trace exporter", t); - return false; - } - } - - @Override - protected boolean doDisable() { - - log.info("Stopping OTLP trace exporter"); - try { - // unregister service - openTelemetryController.unregisterTraceExporterService(this); - if (null != spanExporter) { - spanExporter.close(); - } - } catch (Throwable t) { - log.error("Error disabling OTLP trace exporter", t); - } - return true; - } -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterService.java new file mode 100644 index 0000000000..b7aec88e4b --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterService.java @@ -0,0 +1,121 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; +import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; +import rocks.inspectit.ocelot.config.model.exporters.metrics.OtlpMetricsExporterSettings; + +import javax.validation.Valid; +import java.util.Arrays; +import java.util.List; + +/** + * Service for {@link io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter}/{@link io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter}. + * Can be dynamically started and stopped using the exporters.metrics.otlp.enabled configuration + */ +@Component +@Slf4j +public class OtlpMetricsExporterService extends DynamicallyActivatableMetricsExporterService { + + private final List SUPPORTED_PROTOCOLS = Arrays.asList(TransportProtocol.GRPC, TransportProtocol.HTTP_PROTOBUF); + + /** + * The {@link MetricExporter} for exporting metrics via OTLP + */ + private MetricExporter metricExporter; + + /** + * The {@link PeriodicMetricReaderBuilder} for reading metrics to the log + */ + private PeriodicMetricReaderBuilder metricReaderBuilder; + + public OtlpMetricsExporterService() { + super("metrics.enabled", "exporters.metrics.otlp"); + } + + @Override + protected boolean checkEnabledForConfig(InspectitConfig configuration) { + @Valid OtlpMetricsExporterSettings otlp = configuration.getExporters().getMetrics().getOtlp(); + if (configuration.getMetrics().isEnabled() && !otlp.getEnabled().isDisabled()) { + if (SUPPORTED_PROTOCOLS.contains(otlp.getProtocol())) { + + if (StringUtils.hasText(otlp.getEndpoint())) { + return true; + } else if (StringUtils.hasText(otlp.getUrl())) { + log.warn("You are using the deprecated property 'url'. This property will be invalid in future releases of InspectIT Ocelot, please use 'endpoint' instead."); + return true; + } + } + if (otlp.getEnabled().equals(ExporterEnabledState.ENABLED)) { + if (!SUPPORTED_PROTOCOLS.contains(otlp.getProtocol())) { + log.warn("OTLP Metric Exporter is enabled, but wrong 'protocol' is specified. Supported values are ", Arrays.toString(SUPPORTED_PROTOCOLS.stream() + .map(transportProtocol -> transportProtocol.getName()) + .toArray())); + } + if (!StringUtils.hasText(otlp.getEndpoint()) && !StringUtils.hasText(otlp.getUrl())) { + log.warn("OTLP Metric Exporter is enabled but 'endpoint' is not set."); + } + } + } + return false; + } + + @Override + protected boolean doEnable(InspectitConfig configuration) { + try { + // build and register exporter service + OtlpMetricsExporterSettings otlp = configuration.getExporters().getMetrics().getOtlp(); + String endpoint = StringUtils.hasText(otlp.getEndpoint()) ? otlp.getEndpoint() : otlp.getUrl(); + + switch (otlp.getProtocol()) { + case GRPC: { + metricExporter = OtlpGrpcMetricExporter.builder().setEndpoint(endpoint).build(); + break; + } + case HTTP_PROTOBUF: { + metricExporter = OtlpHttpMetricExporter.builder().setEndpoint(endpoint).build(); + break; + } + } + metricReaderBuilder = PeriodicMetricReader.builder(metricExporter).setInterval(otlp.getExportInterval()); + + boolean success = openTelemetryController.registerMetricExporterService(this); + if (success) { + log.info("Starting {}", getClass().getSimpleName()); + } else { + log.error("Failed to register {} at {}!", getClass().getSimpleName(), openTelemetryController.getClass() + .getSimpleName()); + } + return success; + } catch (Exception e) { + log.error("Error creatig OTLP metrics exporter service", e); + return false; + } + } + + @Override + protected boolean doDisable() { + try { + log.info("Stopping OtlpMetricsExporter"); + openTelemetryController.unregisterMetricExporterService(this); + return true; + } catch (Exception e) { + log.error("Failed to stop OtlpMetricsExporter", e); + return false; + } + } + + @Override + public MetricReaderFactory getNewMetricReaderFactory() { + return metricReaderBuilder.newMetricReaderFactory(); + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java new file mode 100644 index 0000000000..52111db13b --- /dev/null +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java @@ -0,0 +1,105 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; +import rocks.inspectit.ocelot.config.model.exporters.trace.OtlpTraceExporterSettings; + +import javax.validation.Valid; +import java.util.Arrays; +import java.util.List; + +/** + * Service for {@link io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter}/{@link io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter}. + * Can be dynamically started and stopped using the exporters.trace.otlp.enabled configuration + */ +@Component +@Slf4j +public class OtlpTraceExporterService extends DynamicallyActivatableTraceExporterService { + + private final List SUPPORTED_PROTOCOLS = Arrays.asList(TransportProtocol.GRPC, TransportProtocol.HTTP_PROTOBUF); + + @Getter + private SpanExporter spanExporter; + + public OtlpTraceExporterService() { + super("exporters.tracing.otlp", "tracing.enabled"); + } + + @Override + protected boolean checkEnabledForConfig(InspectitConfig configuration) { + @Valid OtlpTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlp(); + if (configuration.getTracing().isEnabled() && !otlp.getEnabled().isDisabled()) { + if (SUPPORTED_PROTOCOLS.contains(otlp.getProtocol())) { + if (StringUtils.hasText(otlp.getEndpoint())) { + return true; + } else if (StringUtils.hasText(otlp.getUrl())) { + log.warn("You are using the deprecated property 'url'. This property will be invalid in future releases of InspectIT Ocelot, please use 'endpoint' instead."); + return true; + } + } + if (otlp.getEnabled().equals(ExporterEnabledState.ENABLED)) { + if (!SUPPORTED_PROTOCOLS.contains(otlp.getProtocol())) { + log.warn("OTLP Trace Exporter is enabled, but wrong 'protocol' is specified. Supported values are ", Arrays.toString(SUPPORTED_PROTOCOLS.stream() + .map(transportProtocol -> transportProtocol.getName()) + .toArray())); + } + if (!StringUtils.hasText(otlp.getEndpoint()) && !StringUtils.hasText(otlp.getUrl())) { + log.warn("OTLP Trace Exporter is enabled but 'endpoint' is not set."); + } + } + } + return false; + } + + @Override + protected boolean doEnable(InspectitConfig configuration) { + try { + OtlpTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlp(); + String endpoint = StringUtils.hasText(otlp.getEndpoint()) ? otlp.getEndpoint() : otlp.getUrl(); + log.info("Starting OTLP Trace Exporter with endpoint {}", endpoint); + + // create span exporter + switch (otlp.getProtocol()) { + case GRPC: { + spanExporter = OtlpGrpcSpanExporter.builder().setEndpoint(endpoint).build(); + break; + } + case HTTP_PROTOBUF: { + spanExporter = OtlpHttpSpanExporter.builder().setEndpoint(endpoint).build(); + break; + } + } + + // register service + openTelemetryController.registerTraceExporterService(this); + return true; + } catch (Throwable t) { + log.error("Error creating OTLP Trace Exporter", t); + return false; + } + } + + @Override + protected boolean doDisable() { + + log.info("Stopping OTLP Trace Exporter"); + try { + // unregister service + openTelemetryController.unregisterTraceExporterService(this); + if (null != spanExporter) { + spanExporter.close(); + } + } catch (Throwable t) { + log.error("Error disabling OTLP Trace Exporter", t); + } + return true; + } +} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java index 8f36a57530..ac73946737 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java @@ -1,8 +1,6 @@ package rocks.inspectit.ocelot.core.exporter; import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter; -import io.opentelemetry.sdk.trace.SpanProcessor; -import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -32,10 +30,13 @@ public ZipkinExporterService() { protected boolean checkEnabledForConfig(InspectitConfig conf) { @Valid ZipkinExporterSettings zipkin = conf.getExporters().getTracing().getZipkin(); if (conf.getTracing().isEnabled() && !zipkin.getEnabled().isDisabled()) { - if (StringUtils.hasText(zipkin.getUrl())) { + if (StringUtils.hasText(zipkin.getEndpoint())) { + return true; + } else if (StringUtils.hasText(zipkin.getUrl())) { + log.warn("You are using the deprecated property 'url'. This property will be invalid in future releases of InspectIT Ocelot, please use 'endpoint' instead."); return true; } else if (zipkin.getEnabled().equals(ExporterEnabledState.ENABLED)) { - log.warn("Zipkin Exporter is enabled but 'url' is not set."); + log.warn("Zipkin Exporter is enabled but 'endpoint' is not set."); } } return false; @@ -45,10 +46,11 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { protected boolean doEnable(InspectitConfig configuration) { try { ZipkinExporterSettings settings = configuration.getExporters().getTracing().getZipkin(); - log.info("Starting Zipkin Exporter with url '{}'", settings.getUrl()); + String endpoint = StringUtils.hasText(settings.getEndpoint()) ? settings.getEndpoint() : settings.getUrl(); + log.info("Starting Zipkin Exporter with endpoint '{}'", endpoint); // create span exporter - spanExporter = ZipkinSpanExporter.builder().setEndpoint(settings.getUrl()).build(); + spanExporter = ZipkinSpanExporter.builder().setEndpoint(endpoint).build(); // register openTelemetryController.registerTraceExporterService(this); diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java index fb4cd63652..740e3aac3b 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java @@ -66,7 +66,7 @@ void verifyInfluxDataWritten() { updateProperties(props -> { props.setProperty("inspectit.exporters.metrics.influx.enabled", true); props.setProperty("inspectit.exporters.metrics.influx.export-interval", "1s"); - props.setProperty("inspectit.exporters.metrics.influx.url", url); + props.setProperty("inspectit.exporters.metrics.influx.endpoint", url); props.setProperty("inspectit.exporters.metrics.influx.database", DATABASE); // note: user and password are mandatory as of v1.15.0 props.setProperty("inspectit.exporters.metrics.influx.user", user); @@ -90,8 +90,7 @@ void verifyInfluxDataWritten() { await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { InfluxDB iDB = InfluxDBFactory.connect(url, user, password); // note: user and password are mandatory as of v1.15.0 - QueryResult result = iDB - .query(new Query("SELECT LAST(cool_data) FROM " + DATABASE + ".autogen.my_test_measure GROUP BY *")); + QueryResult result = iDB.query(new Query("SELECT LAST(cool_data) FROM " + DATABASE + ".autogen.my_test_measure GROUP BY *")); List results = result.getResults(); assertThat(results).hasSize(1); @@ -106,11 +105,11 @@ void verifyInfluxDataWritten() { @DirtiesContext @Test - void testNoUrlSet() { + void testNoEndpointSet() { updateProperties(props -> { - props.setProperty("inspectit.exporters.metrics.influx.url", ""); + props.setProperty("inspectit.exporters.metrics.influx.endpoint", ""); props.setProperty("inspectit.exporters.metrics.influx.enabled", "ENABLED"); }); - warnLogs.assertContains("'url'"); + warnLogs.assertContains("'endpoint'"); } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java index c759b7a4e0..76d8cb62f7 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java @@ -4,76 +4,210 @@ import io.github.netmikey.logunit.api.LogCapturer; import io.opencensus.trace.Tracing; import io.opencensus.trace.samplers.Samplers; +import org.assertj.core.api.AssertionsForClassTypes; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import rocks.inspectit.ocelot.bootstrap.Instances; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; +import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerExporterSettings; import rocks.inspectit.ocelot.core.SpringTestBase; +import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import java.util.concurrent.TimeUnit; import static com.github.tomakehurst.wiremock.client.WireMock.*; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -@TestPropertySource(properties = {"inspectit.exporters.tracing.jaeger.url=http://127.0.0.1:14268/api/traces", "inspectit.exporters.tracing.jaeger.grpc=http://127.0.0.1:14267/api/traces", "inspectit.tracing.max-export-batch-size=1"}) -@DirtiesContext -public class JaegerExporterServiceIntTest extends SpringTestBase { +/** + * Test class for the {@link JaegerExporterService} + */ +public class JaegerExporterServiceIntTest { - private static final Logger logger = LoggerFactory.getLogger(JaegerExporterServiceIntTest.class); + static final int JAEGER_THRIFT_PORT = 14268; - public static final int JAEGER_PORT = 14268; + static final String JAEGER_THRIFT_PATH = "/api/traces"; - public static final String JAEGER_PATH = "/api/traces"; + static final String JAEGER_GRPC_PATH = "/v1/traces"; - private WireMockServer wireMockServer; + private static final Logger logger = LoggerFactory.getLogger(JaegerExporterServiceIntTest.class); @RegisterExtension LogCapturer warnLogs = LogCapturer.create().captureForType(JaegerExporterService.class, org.slf4j.event.Level.WARN); - @BeforeEach - void setupWiremock() { - wireMockServer = new WireMockServer(options().port(JAEGER_PORT)); - wireMockServer.start(); - configureFor(wireMockServer.port()); + /** + * Test for the {@link JaegerExporterService} using the {@link TransportProtocol#HTTP_THRIFT} + */ + @DirtiesContext + @Nested + @TestPropertySource(properties = {"inspectit.exporters.tracing.jaeger.endpoint=http://127.0.0.1:14268/api/traces", "inspectit.exporters.tracing.jaeger.protocol=http/thrift", "inspectit.tracing.max-export-batch-size=1"}) + class JaegerThriftExporterServiceIntTest extends SpringTestBase { - stubFor(post(urlPathEqualTo(JAEGER_PATH)).willReturn(aResponse().withStatus(200))); - } + private WireMockServer wireMockServer; - @AfterEach - void cleanup() { - wireMockServer.stop(); - } + @BeforeEach + void setupWiremock() { + wireMockServer = new WireMockServer(options().port(JAEGER_THRIFT_PORT)); + wireMockServer.start(); + configureFor(wireMockServer.port()); + + stubFor(post(urlPathEqualTo(JAEGER_THRIFT_PATH)).willReturn(aResponse().withStatus(200))); + } + + @AfterEach + void cleanup() { + wireMockServer.stop(); + } - @Test - void verifyTraceSent() throws InterruptedException { - Tracing.getTracer().spanBuilder("jaegerspan").setSampler(Samplers.alwaysSample()).startSpanAndRun(() -> { - }); + @Test + void verifyTraceSent() throws InterruptedException { + Tracing.getTracer().spanBuilder("jaegerspan").setSampler(Samplers.alwaysSample()).startSpanAndRun(() -> { + }); - logger.info("Wait for Jaeger to process the span..."); - Thread.sleep(1100L); + logger.info("Wait for Jaeger to process the span..."); + Thread.sleep(1100L); - Instances.openTelemetryController.flush(); + Instances.openTelemetryController.flush(); + + await().atMost(15, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { + verify(postRequestedFor(urlPathEqualTo(JAEGER_THRIFT_PATH))); + }); + } + } - await().atMost(15, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { - verify(postRequestedFor(urlPathEqualTo(JAEGER_PATH))); - }); + /** + * Test for the {@link JaegerExporterService} using {@link ExporterServiceIntegrationTestBase} + */ + @Nested + class JaegerExporterServiceIntDockerTest extends ExporterServiceIntegrationTestBase { + + @Autowired + JaegerExporterService service; + + @BeforeEach + void clearRequests() { + grpcServer.traceRequests.clear(); + } + + /** + * Test using the {@link TransportProtocol#GRPC} + */ + @Test + @DirtiesContext + void verifyTraceSentGrpc() { + updateProperties(mps -> { + mps.setProperty("inspectit.exporters.tracing.jaeger.enabled", ExporterEnabledState.ENABLED); + mps.setProperty("inspectit.exporters.tracing.jaeger.endpoint", getEndpoint(COLLECTOR_JAEGER_GRPC_PORT, JAEGER_GRPC_PATH)); + mps.setProperty("inspectit.exporters.tracing.jaeger.protocol", TransportProtocol.GRPC); + }); + await().atMost(15, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + + makeSpansAndFlush("jaeger-grpc-parent", "jaeger-grpc-child"); + + awaitSpansExported("jaeger-grpc-parent", "jaeger-grpc-child"); + /* await().atMost(15, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(grpcServer.traceRequests).hasSize(1));*/ + } + + /** + * Test using the {@link TransportProtocol#HTTP_THRIFT} + */ + @Test + @DirtiesContext + void verifyTraceSentThrift() { + updateProperties(mps -> { + mps.setProperty("inspectit.exporters.tracing.jaeger.enabled", ExporterEnabledState.ENABLED); + mps.setProperty("inspectit.exporters.tracing.jaeger.endpoint", getEndpoint(COLLECTOR_JAEGER_THRIFT_HTTP_PORT, JAEGER_THRIFT_PATH)); + mps.setProperty("inspectit.exporters.tracing.jaeger.protocol", TransportProtocol.HTTP_THRIFT); + }); + await().atMost(15, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + + makeSpansAndFlush("jaeger-thrift-parent", "jaeger-thrift-child"); + + awaitSpansExported("jaeger-thrift-parent", "jaeger-thrift-child"); + } } + /** + * Tests for testing the {@link JaegerExporterSettings} in {@link rocks.inspectit.ocelot.config.model.exporters.trace.TraceExportersSettings#jaeger} + */ @DirtiesContext - @Test - void testNoUrlSet() { - updateProperties(props -> { - props.setProperty("inspectit.exporters.tracing.jaeger.url", ""); - props.setProperty("inspectit.exporters.tracing.jaeger.enabled", ExporterEnabledState.ENABLED); - }); - warnLogs.assertContains("'url'"); + @Nested + class JaegerSettingsIntTest extends SpringTestBase { + + @Autowired + JaegerExporterService service; + + @Autowired + InspectitEnvironment environment; + + @DirtiesContext + @Test + void testEndpointNotSet() { + updateProperties(props -> { + props.setProperty("inspectit.exporters.tracing.jaeger.endpoint", ""); + props.setProperty("inspectit.exporters.tracing.jaeger.enabled", ExporterEnabledState.ENABLED); + }); + warnLogs.assertContains("'endpoint'"); + } + + @DirtiesContext + @Test + void testProtocolNotSet() { + updateProperties(props -> { + props.setProperty("inspectit.exporters.tracing.jaeger.protocol", ""); + props.setProperty("inspectit.exporters.tracing.jaeger.enabled", ExporterEnabledState.ENABLED); + }); + warnLogs.assertContains("protocol"); + } + + /** + * Test the default {@link rocks.inspectit.ocelot.config.model.exporters.trace.JaegerExporterSettings settings} of the {@link JaegerExporterService} + */ + @Test + void testDefault() { + // service should be disabled + assertThat(service.isEnabled()).isFalse(); + // enabled flag should be IF_CONFIGURED + assertThat(environment.getCurrentConfig() + .getExporters() + .getTracing() + .getJaeger() + .getEnabled()).isEqualTo(ExporterEnabledState.IF_CONFIGURED); + // endpoint should be null or empty + assertThat(environment.getCurrentConfig() + .getExporters() + .getTracing() + .getJaeger() + .getEndpoint()).isNullOrEmpty(); + // TODO: what is the default for protocol? + } + + @Test + void defaultSettings() { + // service is not running + AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); + + JaegerExporterSettings jaeger = environment.getCurrentConfig().getExporters().getTracing().getJaeger(); + // enabled property is set to IF_CONFIGURED + AssertionsForClassTypes.assertThat(jaeger.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); + // endpoint is null or empty + AssertionsForClassTypes.assertThat(jaeger.getEndpoint()).isNullOrEmpty(); + } } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java deleted file mode 100644 index 407b8e5122..0000000000 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerGrpcExporterServiceIntTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.github.netmikey.logunit.api.LogCapturer; -import org.assertj.core.api.AssertionsForClassTypes; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.annotation.DirtiesContext; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; -import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerGrpcExporterSettings; - -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - -/** - * Test for the {@link JaegerGrpcExporterService} - */ -public class JaegerGrpcExporterServiceIntTest extends ExporterServiceIntegrationTestBase { - - public static String JAEGER_GRPC_PATH = "/v1/traces"; - - @RegisterExtension - LogCapturer warnLogs = LogCapturer.create() - .captureForType(JaegerGrpcExporterService.class, org.slf4j.event.Level.WARN); - - @Autowired - JaegerGrpcExporterService service; - - @Test - @DirtiesContext - void verifyTraceSent() { - updateProperties(mps -> { - mps.setProperty("inspectit.exporters.tracing.jaeger-grpc.enabled", ExporterEnabledState.ENABLED); - mps.setProperty("inspectit.exporters.tracing.jaeger-grpc.grpc", getEndpoint(COLLECTOR_JAEGER_GRPC_PORT, JAEGER_GRPC_PATH)); - }); - await().atMost(15, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); - - makeSpansAndFlush("jaeger-grpc-parent", "jaeger-grpc-child"); - - await().atMost(15, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(grpcServer.traceRequests).hasSize(1)); - } - - /** - * Test the default {@link rocks.inspectit.ocelot.config.model.exporters.trace.JaegerGrpcExporterSettings settings} of the {@link JaegerGrpcExporterService} - */ - @Test - void testDefault() { - // service should be disabled - assertThat(service.isEnabled()).isFalse(); - // enabled flag should be IF_CONFIGURED - assertThat(environment.getCurrentConfig() - .getExporters() - .getTracing() - .getJaegerGrpc() - .getEnabled()).isEqualTo(ExporterEnabledState.IF_CONFIGURED); - // gRPC API endpoint should be null or empty - assertThat(environment.getCurrentConfig() - .getExporters() - .getTracing() - .getJaegerGrpc() - .getGrpc()).isNullOrEmpty(); - } - - @DirtiesContext - @Test - void testNoUrlSet() { - updateProperties(props -> { - props.setProperty("inspectit.exporters.tracing.jaeger-grpc.grpc", ""); - props.setProperty("inspectit.exporters.tracing.jaeger-grpc.enabled", ExporterEnabledState.ENABLED); - }); - warnLogs.assertContains("'grpc'"); - } - - @Test - void defaultSettings() { - // service is not running - AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); - - JaegerGrpcExporterSettings jaegerGrpc = environment.getCurrentConfig() - .getExporters() - .getTracing() - .getJaegerGrpc(); - // enabled property is set to IF_CONFIGURED - AssertionsForClassTypes.assertThat(jaegerGrpc.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); - // url is null or empty - AssertionsForClassTypes.assertThat(jaegerGrpc.getUrl()).isNullOrEmpty(); - } -} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java deleted file mode 100644 index 70d7d8d103..0000000000 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerThriftHttpExporterServiceIntTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.github.netmikey.logunit.api.LogCapturer; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.annotation.DirtiesContext; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; - -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - -/** - * Test for {@link JaegerExporterService} using the {@link ExporterServiceIntegrationTestBase} - */ -@DirtiesContext -public class JaegerThriftHttpExporterServiceIntTest extends ExporterServiceIntegrationTestBase { - - public static final String JAEGER_PATH = "/api/traces"; - - @Autowired - JaegerExporterService service; - - @RegisterExtension - LogCapturer warnLogs = LogCapturer.create().captureForType(JaegerExporterService.class, org.slf4j.event.Level.WARN); - - @DirtiesContext - @Test - void verifyTraceSent() { - - // enable Jaeger Thrift exporter - updateProperties(mps -> { - mps.setProperty("inspectit.exporters.tracing.jaeger.enabled", ExporterEnabledState.ENABLED); - mps.setProperty("inspectit.exporters.tracing.jaeger.url", getEndpoint(COLLECTOR_JAEGER_THRIFT_HTTP_PORT, JAEGER_PATH)); - }); - await().atMost(15, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); - - makeSpansAndFlush("jaeger-thrift-parent", "jaeger-thrift-child"); - - await().atMost(15, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(grpcServer.traceRequests).hasSize(1)); - - } - - @DirtiesContext - @Test - void testNoUrlSet() { - updateProperties(props -> { - props.setProperty("inspectit.exporters.tracing.jaeger.url", ""); - props.setProperty("inspectit.exporters.tracing.jaeger.enabled", ExporterEnabledState.ENABLED); - }); - warnLogs.assertContains("'url'"); - } - -} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java deleted file mode 100644 index a654c30c5a..0000000000 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcMetricsExporterServiceIntTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.github.netmikey.logunit.api.LogCapturer; -import org.assertj.core.api.AssertionsForClassTypes; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.annotation.DirtiesContext; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; -import rocks.inspectit.ocelot.config.model.exporters.metrics.OtlpGrpcMetricsExporterSettings; -import rocks.inspectit.ocelot.core.config.InspectitEnvironment; - -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - -/** - * Test class for {@link OtlpGrpcMetricsExporterService} - */ -public class OtlpGrpcMetricsExporterServiceIntTest extends ExporterServiceIntegrationTestBase { - - public static final String OTLP_GRPC_METRICS_PATH = "/v1/metrics"; - - @RegisterExtension - LogCapturer warnLogs = LogCapturer.create() - .captureForType(OtlpGrpcMetricsExporterService.class, org.slf4j.event.Level.WARN); - - @Autowired - OtlpGrpcMetricsExporterService service; - - @Autowired - InspectitEnvironment environment; - - String tagKey = "otlp-grpc-metrics-test"; - - String tagVal = "random-val"; - - int metricVal = 1337; - - @DirtiesContext - @Test - void verifyMetricsWritten() { - updateProperties(mps -> { - mps.setProperty("inspectit.exporters.metrics.otlp-grpc.url", getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_GRPC_METRICS_PATH)); - mps.setProperty("inspectit.exporters.metrics.otlp-grpc.export-interval", "500ms"); - mps.setProperty("inspectit.exporters.metrics.otlp-grpc.enabled", ExporterEnabledState.ENABLED); - }); - - await().atMost(5, TimeUnit.SECONDS) - .pollInterval(500, TimeUnit.MILLISECONDS) - .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); - - recordMetricsAndFlush(metricVal, tagKey, tagVal); - - awaitMetricsExported(metricVal, tagKey, tagVal); - } - - @DirtiesContext - @Test - void testNoUrlSet() { - updateProperties(props -> { - props.setProperty("inspectit.exporters.metrics.otlp-grpc.url", ""); - props.setProperty("inspectit.exporters.metrics.otlp-grpc.enabled", ExporterEnabledState.ENABLED); - }); - warnLogs.assertContains("'url'"); - } - - @Test - void defaultSettings() { - // service is not running - AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); - - OtlpGrpcMetricsExporterSettings otlpGrpc = environment.getCurrentConfig() - .getExporters() - .getMetrics() - .getOtlpGrpc(); - // enabled property is set to IF_CONFIGURED - AssertionsForClassTypes.assertThat(otlpGrpc.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); - // url is null or empty - AssertionsForClassTypes.assertThat(otlpGrpc.getUrl()).isNullOrEmpty(); - } -} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java deleted file mode 100644 index a7eeca5ec6..0000000000 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpGrpcTraceExporterServiceIntTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.github.netmikey.logunit.api.LogCapturer; -import org.assertj.core.api.AssertionsForClassTypes; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.annotation.DirtiesContext; -import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; -import rocks.inspectit.ocelot.config.model.exporters.metrics.OtlpGrpcMetricsExporterSettings; -import rocks.inspectit.ocelot.config.model.exporters.trace.OtlpGrpcTraceExporterSettings; - -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - -/** - * Test for the {@link OtlpGrpcTraceExporterService} - */ -@DirtiesContext -public class OtlpGrpcTraceExporterServiceIntTest extends ExporterServiceIntegrationTestBase { - - public static final String OTLP_GRPC_TRACING_PATH = "/v1/trace"; - - @RegisterExtension - LogCapturer warnLogs = LogCapturer.create().captureForType(OtlpGrpcTraceExporterService.class, org.slf4j.event.Level.WARN); - - @Autowired - private OtlpGrpcTraceExporterService service; - - @DirtiesContext - @Test - void verifyTraceSent() { - updateProperties(properties -> { - properties.setProperty("inspectit.exporters.tracing.otlp-grpc.url", getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_GRPC_TRACING_PATH)); - properties.setProperty("inspectit.exporters.tracing.otlp-grpc.enabled", ExporterEnabledState.ENABLED); - }); - - await().atMost(10, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); - - makeSpansAndFlush("otlp-grpc-parent", "otlp-grpc-child"); - - awaitSpansExported("otlp-grpc-parent", "otlp-grpc-child"); - } - - @DirtiesContext - @Test - void testNoUrlSet() { - updateProperties(props -> { - props.setProperty("inspectit.exporters.tracing.otlp-grpc.url", ""); - props.setProperty("inspectit.exporters.tracing.otlp-grpc.enabled", ExporterEnabledState.ENABLED); - }); - warnLogs.assertContains("'url'"); - } - - @Test - void defaultSettings() { - // service is not running - AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); - - OtlpGrpcTraceExporterSettings otlpGrpc = environment.getCurrentConfig() - .getExporters() - .getTracing() - .getOtlpGrpc(); - // enabled property is set to IF_CONFIGURED - AssertionsForClassTypes.assertThat(otlpGrpc.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); - // url is null or empty - AssertionsForClassTypes.assertThat(otlpGrpc.getUrl()).isNullOrEmpty(); - } - -} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java new file mode 100644 index 0000000000..24e3503d5d --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java @@ -0,0 +1,121 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.github.netmikey.logunit.api.LogCapturer; +import org.assertj.core.api.AssertionsForClassTypes; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; +import rocks.inspectit.ocelot.config.model.exporters.metrics.OtlpMetricsExporterSettings; +import rocks.inspectit.ocelot.core.config.InspectitEnvironment; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +/** + * Test class for {@link OtlpMetricsExporterService} + */ +public class OtlpMetricsExporterServiceIntTest extends ExporterServiceIntegrationTestBase { + + public static final String OTLP_METRICS_PATH = "/v1/metrics"; + + @RegisterExtension + LogCapturer warnLogs = LogCapturer.create() + .captureForType(OtlpMetricsExporterService.class, org.slf4j.event.Level.WARN); + + @Autowired + OtlpMetricsExporterService service; + + @Autowired + InspectitEnvironment environment; + + String tagKeyGrpc = "otlp-grpc-metrics-test"; + + String tagKeyHttp = "otlp-grpc-metrics-test"; + + String tagVal = "random-val"; + + int metricVal = 1337; + + @BeforeEach + void clearRequests() { + grpcServer.metricRequests.clear(); + } + + @DirtiesContext + @Test + void verifyMetricsWrittenGrpc() { + updateProperties(mps -> { + mps.setProperty("inspectit.exporters.metrics.otlp.url", getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_METRICS_PATH)); + mps.setProperty("inspectit.exporters.metrics.otlp.export-interval", "500ms"); + mps.setProperty("inspectit.exporters.metrics.otlp.enabled", ExporterEnabledState.ENABLED); + mps.setProperty("inspectit.exporters.metrics.otlp.protocol", TransportProtocol.GRPC); + }); + + await().atMost(5, TimeUnit.SECONDS) + .pollInterval(500, TimeUnit.MILLISECONDS) + .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + + recordMetricsAndFlush(metricVal, tagKeyGrpc, tagVal); + + awaitMetricsExported(metricVal, tagKeyGrpc, tagVal); + } + + @DirtiesContext + @Test + void verifyMetricsWrittenHttp() { + updateProperties(mps -> { + mps.setProperty("inspectit.exporters.metrics.otlp.url", getEndpoint(COLLECTOR_OTLP_HTTP_PORT, OTLP_METRICS_PATH)); + mps.setProperty("inspectit.exporters.metrics.otlp.export-interval", "500ms"); + mps.setProperty("inspectit.exporters.metrics.otlp.enabled", ExporterEnabledState.ENABLED); + mps.setProperty("inspectit.exporters.metrics.otlp.protocol", TransportProtocol.HTTP_PROTOBUF); + }); + + await().atMost(5, TimeUnit.SECONDS) + .pollInterval(500, TimeUnit.MILLISECONDS) + .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + + recordMetricsAndFlush(metricVal, tagKeyHttp, tagVal); + + awaitMetricsExported(metricVal, tagKeyHttp, tagVal); + } + + @DirtiesContext + @Test + void testNoEndpointSet() { + updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.otlp.endpoint", ""); + props.setProperty("inspectit.exporters.metrics.otlp.enabled", ExporterEnabledState.ENABLED); + }); + warnLogs.assertContains("'endpoint'"); + } + + @DirtiesContext + @Test + void testNoProtocolSet() { + updateProperties(props -> { + props.setProperty("inspectit.exporters.metrics.otlp.protocol", ""); + props.setProperty("inspectit.exporters.metrics.otlp.enabled", ExporterEnabledState.ENABLED); + }); + warnLogs.assertContains("'protocol'"); + } + + @Test + void defaultSettings() { + // service is not running + AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); + + OtlpMetricsExporterSettings otlp = environment.getCurrentConfig().getExporters().getMetrics().getOtlp(); + // enabled property is set to IF_CONFIGURED + assertThat(otlp.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); + // endpoint is null or empty + assertThat(otlp.getEndpoint()).isNullOrEmpty(); + // protocol is GRPC + assertThat(otlp.getProtocol()).isEqualTo(TransportProtocol.GRPC); + } +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java new file mode 100644 index 0000000000..32d1e0506b --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java @@ -0,0 +1,107 @@ +package rocks.inspectit.ocelot.core.exporter; + +import io.github.netmikey.logunit.api.LogCapturer; +import org.assertj.core.api.AssertionsForClassTypes; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; +import rocks.inspectit.ocelot.config.model.exporters.trace.OtlpTraceExporterSettings; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +/** + * Test for the {@link OtlpTraceExporterService} + */ +@DirtiesContext +public class OtlpTraceExporterServiceIntTest extends ExporterServiceIntegrationTestBase { + + public static final String OTLP_GRPC_TRACING_PATH = "/v1/trace"; + + @RegisterExtension + LogCapturer warnLogs = LogCapturer.create() + .captureForType(OtlpTraceExporterService.class, org.slf4j.event.Level.WARN); + + @Autowired + private OtlpTraceExporterService service; + + @BeforeEach + void clearRequests() { + grpcServer.traceRequests.clear(); + } + + @DirtiesContext + @Test + void verifyTraceSentGrpc() { + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.tracing.otlp.protocol", TransportProtocol.GRPC); + properties.setProperty("inspectit.exporters.tracing.otlp.endpoint", getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_GRPC_TRACING_PATH)); + properties.setProperty("inspectit.exporters.tracing.otlp.enabled", ExporterEnabledState.ENABLED); + }); + + await().atMost(10, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + + makeSpansAndFlush("otlp-grpc-parent", "otlp-grpc-child"); + + awaitSpansExported("otlp-grpc-parent", "otlp-grpc-child"); + } + + @DirtiesContext + @Test + void verifyTraceSentHttp() { + updateProperties(properties -> { + properties.setProperty("inspectit.exporters.tracing.otlp.protocol", TransportProtocol.HTTP_PROTOBUF); + properties.setProperty("inspectit.exporters.tracing.otlp.endpoint", getEndpoint(COLLECTOR_OTLP_HTTP_PORT, OTLP_GRPC_TRACING_PATH)); + properties.setProperty("inspectit.exporters.tracing.otlp.enabled", ExporterEnabledState.ENABLED); + }); + + await().atMost(10, TimeUnit.SECONDS) + .pollInterval(1, TimeUnit.SECONDS) + .untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); + + makeSpansAndFlush("otlp-http-parent", "otlp-http-child"); + + awaitSpansExported("otlp-http-parent", "otlp-http-child"); + } + + @DirtiesContext + @Test + void testNoEndpointSet() { + updateProperties(props -> { + props.setProperty("inspectit.exporters.tracing.otlp.endpoint", ""); + props.setProperty("inspectit.exporters.tracing.otlp.enabled", ExporterEnabledState.ENABLED); + }); + warnLogs.assertContains("'endpoint'"); + } + + @DirtiesContext + @Test + void testNoProtocolSet() { + updateProperties(props -> { + props.setProperty("inspectit.exporters.tracing.otlp.protocol", ""); + props.setProperty("inspectit.exporters.tracing.otlp.enabled", ExporterEnabledState.ENABLED); + }); + warnLogs.assertContains("'protocol'"); + } + + @Test + void defaultSettings() { + // service is not running + AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); + + OtlpTraceExporterSettings otlpGrpc = environment.getCurrentConfig().getExporters().getTracing().getOtlp(); + // enabled property is set to IF_CONFIGURED + AssertionsForClassTypes.assertThat(otlpGrpc.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); + // url is null or empty + AssertionsForClassTypes.assertThat(otlpGrpc.getUrl()).isNullOrEmpty(); + } + +} diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java index 5aa49b3855..f7d5dff394 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceInt2Test.java @@ -1,16 +1,13 @@ package rocks.inspectit.ocelot.core.exporter; -import io.github.netmikey.logunit.api.LogCapturer; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.slf4j.event.LoggingEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import java.util.concurrent.TimeUnit; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.awaitility.Awaitility.await; /** @@ -20,10 +17,6 @@ public class ZipkinExporterServiceInt2Test extends ExporterServiceIntegrationTes private static final String ZIPKIN_PATH = "/api/v2/spans"; - @RegisterExtension - LogCapturer warnLogs = LogCapturer.create() - .captureForType(ZipkinExporterService.class, org.slf4j.event.Level.WARN); - @Autowired ZipkinExporterService service; @@ -32,7 +25,7 @@ public class ZipkinExporterServiceInt2Test extends ExporterServiceIntegrationTes void verifyTraceSent() { updateProperties(mps -> { mps.setProperty("inspectit.exporters.tracing.zipkin.enabled", ExporterEnabledState.ENABLED); - mps.setProperty("inspectit.exporters.tracing.zipkin.url", getEndpoint(COLLECTOR_ZIPKIN_PORT, ZIPKIN_PATH)); + mps.setProperty("inspectit.exporters.tracing.zipkin.endpoint", getEndpoint(COLLECTOR_ZIPKIN_PORT, ZIPKIN_PATH)); }); await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(service.isEnabled()).isTrue()); @@ -43,16 +36,6 @@ void verifyTraceSent() { } - @DirtiesContext - @Test - void testNoUrlSet() { - updateProperties(props -> { - props.setProperty("inspectit.exporters.tracing.zipkin.url", ""); - props.setProperty("inspectit.exporters.tracing.zipkin.enabled", ExporterEnabledState.ENABLED); - }); - warnLogs.assertContains("'url'"); - } - } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java index 3abcab700f..781ae5329f 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java @@ -22,7 +22,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.awaitility.Awaitility.await; -@TestPropertySource(properties = {"inspectit.exporters.tracing.zipkin.url=http://127.0.0.1:9411/api/v2/spans", "inspectit.tracing.max-export-batch-size=512", "inspectit.tracing.schedule-delay-millis=20000"}) +@TestPropertySource(properties = {"inspectit.exporters.tracing.zipkin.endpoint=http://127.0.0.1:9411/api/v2/spans", "inspectit.tracing.max-export-batch-size=512", "inspectit.tracing.schedule-delay-millis=20000"}) @DirtiesContext public class ZipkinExporterServiceIntTest extends SpringTestBase { @@ -32,11 +32,11 @@ public class ZipkinExporterServiceIntTest extends SpringTestBase { private static final String ZIPKIN_PATH = "/api/v2/spans"; - private WireMockServer wireMockServer; - @RegisterExtension LogCapturer warnLogs = LogCapturer.create().captureForType(ZipkinExporterService.class, org.slf4j.event.Level.WARN); + private WireMockServer wireMockServer; + @BeforeEach void setupWiremock() { wireMockServer = new WireMockServer(options().port(ZIPKIN_PORT)); @@ -68,11 +68,11 @@ void verifyTraceSent() throws InterruptedException { @DirtiesContext @Test - void testNoUrlSet() { + void testNoEndpointSet() { updateProperties(props -> { - props.setProperty("inspectit.exporters.tracing.zipkin.url", ""); + props.setProperty("inspectit.exporters.tracing.zipkin.endpoint", ""); props.setProperty("inspectit.exporters.tracing.zipkin.enabled", ExporterEnabledState.ENABLED); }); - warnLogs.assertContains("'url'"); + warnLogs.assertContains("'endpoint'"); } } diff --git a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md index 7669d53dea..69ebf2bc03 100644 --- a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md +++ b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md @@ -6,14 +6,25 @@ title: Breaking Changes ### Integration of the OpenTelemetry OpenCensus Shim -Starting with the current release, inspectIT Ocelot migrates from OpenCensus to [OpenTelemetry](https://github.com/open-telemetry). As a first step, we include the [OpenTelemetry OpenCensus Shim](https://github.com/open-telemetry/opentelemetry-java/tree/main/opencensus-shim). inspectIT Ocelot still uses and supports the [OpenCensus-API](https://opencensus.io/quickstart/java/), but the exporter implementations of OpenTelemetry are used. +Starting with the current release, inspectIT Ocelot migrates from OpenCensus to [OpenTelemetry](https://github.com/open-telemetry). As a first step, we include the [OpenTelemetry OpenCensus Shim](https://github.com/open-telemetry/opentelemetry-java/tree/main/opencensus-shim). InspectIT Ocelot still uses and supports the [OpenCensus-API](https://opencensus.io/quickstart/java/), but the exporter implementations of OpenTelemetry are used. -### Updated trace and metric exporter services +### Updated and removed exporter -Due to the migration from OpenCensus to OpenTelemetry, several trace and metric exporter services are disabled or have been removed. The only exporters supported in the current version are `Logging` [metric](metrics/metric-exporters.md#logging) and [trace](tracing/tracing-exporters.md#logging) exporters. +#### Removed `OpenCensusAgentExporter` -The [Jaeger](tracing/tracing-exporters.md#jaeger-exporter) and [Zipkin](tracing/tracing-exporters.md#zipking-exporter) trace exporters as well as the [Prometheus](metrics/metric-exporters.md#prometheus-exporter) and [InfluxDB](metrics/metric-exporters.md#influxdb-exporter) metric exporters are disabled until they are properly integrated with OpenTelemetry. -The `OpenCensus Agent Exporter` (for metrics and traces) has been completely removed and will not be supported in the future. +Due to the migration from OpenCensus to OpenTelemetry, the `OpenCensus Agent Exporter` (for metrics and traces) has been removed and will not be supported in the future. + +#### Added `OTLPMetricsExporter` and `OTLPTraceExporter` + +Due to the migration to OpenTelemetry, InspectIT Ocelot now supports OpenTelemetry Protocol (OTLP) exporter for metrics and tracing. + +#### Exporter property `url` replaced by `endpoint` + +Due to the migration to OpenTelemetry, we approach the naming of OpenTelemetry for the exporters' properties. For this, the previously used property `url` is replaced by the property `endpoint`. The deprecated property `url` is still supported in this release but will be removed in future releases. + +#### New property `protocol` for Jaeger and OTLP exporter + +This release introduces the property `protocol` for the Jaeger and OpenTelemetry Protocol (OTLP) exporter. In case for Jaeger, supported protocols are `http/thrift` and `grpc`. For OTLP, supported protocols are `grpc` and `http/protobuf`. ## Breaking changes in 1.15.0 diff --git a/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md b/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md index 0d05fdded5..0db8a2eebf 100644 --- a/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md +++ b/inspectit-ocelot-documentation/docs/metrics/metric-exporters.md @@ -62,29 +62,34 @@ This can greatly reduce the amount of data written into the InfluxDB, especially The following properties are nested properties below the `inspectit.exporters.metrics.influx` property: -|Property | Default | Description +|Property | Default | Description| |---|-----------------------------------------|---| -|`.enabled`| `IF_CONFIGURED` |If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Influx exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`. -|`.url`| `null` |The HTTP url of the InfluxDB, e.g. `http://localhost:8086`. -|`.user`| `null` | The user to use for connecting to the InfluxDB, can not be empty. -|`.password`| `null` |The password to use for connecting to the InfluxDB, can be not be empty. -|`.database`| `inspectit` | The InfluxDB database to which the metrics are pushed. -|`.retention-policy`| `autogen` | The retention policy of the database to use for writing metrics. -|`.create-database`| `true` | If enabled, the database defined by the `database` property is automatically created on startup with an `autogen` retention policy if it does not exist yet. -|`.export-interval`| refers to `inspectit.metrics.frequency` |Defines how often metrics are pushed to the InfluxDB. -|`.counters-as-differences`| `true` |Defines whether counters are exported using their absolute value or as the increase between exports -|`buffer-size`| `40` | In case the InfluxDB is not reachable, failed writes will be buffered and written on the next export. This value defines the maximum number of batches to buffer. +|`.enabled`| `IF_CONFIGURED` |If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Influx exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`.| +|`.endpoint`| `null` |The HTTP endpoint of the InfluxDB, e.g. `http://localhost:8086`.| +|`.user`| `null` | The user to use for connecting to the InfluxDB, can not be empty.| +|`.password`| `null` |The password to use for connecting to the InfluxDB, can be not be empty.| +|`.database`| `inspectit` | The InfluxDB database to which the metrics are pushed.| +|`.retention-policy`| `autogen` | The retention policy of the database to use for writing metrics.| +|`.create-database`| `true` | If enabled, the database defined by the `database` property is automatically created on startup with an `autogen` retention policy if it does not exist yet.| +|`.export-interval`| refers to `inspectit.metrics.frequency` |Defines how often metrics are pushed to the InfluxDB.| +|`.counters-as-differences`| `true` |Defines whether counters are exported using their absolute value or as the increase between exports| +|`buffer-size`| `40` | In case the InfluxDB is not reachable, failed writes will be buffered and written on the next export. This value defines the maximum number of batches to buffer.| ## OTLP Exporter (Metrics) The OpenTelemetry Protocol (OTLP) exporters export the metrics to the desired endpoint at a specified interval. To enable the OTLP exporters, it is only required to specify the `url`. -### OTLP gRPC Exporter - The following properties are nested properties below the `inspectit.exporters.metrics.otlp-grpc` property: -| Property | Default | Description | -| ---------- | ---------- | ------------------------------------------------------------ | -| `.enabled` | `DISABLED` | If `ENABLED` or `IF_CONFIGURED`, the inspectIT Ocelot agent will try to start the OTLP gRPC metrics exporter. | -| `.url` | `null` | The OTLP gRPC endpoint to connect to, e.g. `http://localhost:4317` | \ No newline at end of file +| Property | Default | Description | +| ----------- | ---------- | ------------------------------------------------------------ | +| `.enabled` | `DISABLED` | If `ENABLED` or `IF_CONFIGURED`, the inspectIT Ocelot agent will try to start the OTLP gRPC metrics exporter. | +| `.endpoint` | `null` | The OTLP endpoint to connect to, e.g. `http://localhost:4317` | +| `.protocol` | `grpc` | The transport protocol, see [OTEL documentation](https://opentelemetry.io/docs/reference/specification/protocol/exporter/). Supported protocols are `grpc` and `http/protobuf`. | + +To make inspectIT Ocelot push the metris via OTLP to, e.g. an OpenTelemetry Collector running on the same machine as the agent, the following JVM property can be used: + +``` +-Dinspectit.exporters.metrics.otlp.endpoint=http://127.0.0.1:4317 +``` \ No newline at end of file diff --git a/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md b/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md index 37c2acf801..fd56c8547a 100644 --- a/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md +++ b/inspectit-ocelot-documentation/docs/tracing/trace-exporters.md @@ -39,7 +39,7 @@ The following properties are nested properties below the `inspectit.exporters.tr |Property |Default| Description| |---|---|---| |`.enabled`|`IF_CONFIGURED`|If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Zipkin exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`.| -|`.url`|`null`|v2 URL under which the ZipKin server can be accessed (e.g. http://127.0.0.1:9411/api/v2/spans).| +|`.endpoint`|`null`|v2 URL under which the ZipKin server can be accessed (e.g. http://127.0.0.1:9411/api/v2/spans).| To make inspectIT Ocelot push the spans to a Zipkin server running on the same machine as the agent, the following JVM property can be used: @@ -60,39 +60,30 @@ The following properties are nested properties below the `inspectit.exporters.tr |Property |Default| Description| |---|---|---| |`.enabled`|`IF_CONFIGURED`|If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Jaeger exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`.| -|`.url`|`null`|URL under which the Jaeger thrift server can be accessed (e.g. http://127.0.0.1:14268/api/traces).| +|`.endpoint`|`null`|URL endpoint under which the Jaeger server can be accessed (e.g. http://127.0.0.1:14268/api/traces).| +|`.protocol`|`grpc`|The transport protocol. Supported protocols are `grpc` and `http/thrift`.| To make inspectIT Ocelot push the spans to a Jaeger server running on the same machine as the agent, the following JVM property can be used: ``` --Dinspectit.exporters.tracing.jaeger.url=http://127.0.0.1:14268/api/traces -``` - -### Jaeger gRPC Exporter - -The following properties are nested properties below the `inspectit.exporters.tracing.jaeger-grpc` property: - -| Property | Default | Description | -| ---------- | --------------- | ------------------------------------------------------------ | -| `.enabled` | `IF_CONFIGURED` | If `ENABLED` or `IF_CONFIGURED`, the agent will try to start the Jaeger exporter. If the url is not set, it will log a warning if set to `ENABLED` but fail silently if set to `IF_CONFIGURED`. | -| `.grpc` | `null` | gRPC endpoint under which the Jaeger gRPC server can be accessed (e.g. http://127.0.0.1:14250/v1/traces). | - -To make inspectIT Ocelot push the spans to a Jaeger server running on the same machine as the agent, the following JVM property can be used: - -``` --Dinspectit.exporters.tracing.jaeger-grpc.url=http://127.0.0.1:14250/v1/traces +-Dinspectit.exporters.tracing.jaeger.endpoint=http://127.0.0.1:14268/api/traces ``` ## OTLP Exporter (Traces) The OpenTelemetry Protocol (OTLP) exporters export the Traces in OTLP to the desired endpoint at a specified interval. -By default, the OTLP exporters are enabled but the URL needed for the exporter to actually start is set to `null`. +By default, the OTLP exporters are enabled but the URL endpoint needed for the exporter to actually start is set to `null`. -## OTLP gRPC Exporter +The following properties are nested properties below the `inspectit.exporters.traces.otlp` property: -The following properties are nested properties below the `inspectit.exporters.traces.otlp-grpc` property: +| Property | Default | Description | +| ----------- | ---------- | ------------------------------------------------------------ | +| `.enabled` | `DISABLED` | If `ENABLED` or `IF_CONFIGURED`, the inspectIT Ocelot agent will try to start the OTLP gRPC trace exporter. | +| `.endpoint` | `null` | The OTLP endpoint to connect to, e.g. `http://localhost:4317` | +| `.protocol` | `grpc` | The transport protocol, see [OTEL documentation](https://opentelemetry.io/docs/reference/specification/protocol/exporter/). Supported protocols are `grpc` and `http/protobuf`. | -| Property | Default | Description | -| ---------- | ---------- | ------------------------------------------------------------ | -| `.enabled` | `DISABLED` | If `ENABLED` or `IF_CONFIGURED`, the inspectIT Ocelot agent will try to start the OTLP gRPC trace exporter. | -| `.url` | `null` | The OTLP gRPC endpoint to connect to, e.g. `http://localhost:4317` | +To make inspectIT Ocelot push the spans via OTLP to, e.g. an OpenTelemetry Collector running on the same machine as the agent, the following JVM property can be used: + +``` +-Dinspectit.exporters.tracing.otlp.endpoint=http://127.0.0.1:4318 +``` From bbb740f81a5beabb67977249fcca93d6f1876ccb Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Mon, 28 Mar 2022 11:28:35 +0200 Subject: [PATCH 72/86] feat(eum): [#1297] replace `grpc` property in `application.yml` and report in breaking changes --- .../src/main/resources/application.yml | 6 +++++- .../docs/breaking-changes/breaking-changes.md | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/components/inspectit-ocelot-eum-server/src/main/resources/application.yml b/components/inspectit-ocelot-eum-server/src/main/resources/application.yml index 442b0f1cc5..ff9df6a742 100644 --- a/components/inspectit-ocelot-eum-server/src/main/resources/application.yml +++ b/components/inspectit-ocelot-eum-server/src/main/resources/application.yml @@ -172,10 +172,14 @@ inspectit-eum-server: # If jaeger exporter for the OT received spans is enabled. enabled: IF_CONFIGURED + # The transport protocol + protocol: grpc + # Location of the jaeger gRPC API. # Either a valid NameResolver-compliant URI, or an authority string. # If this property is not set, the jaeger-exporter will not be started. - # grpc: localhost:14250 + # endpoint: localhost:14250 + # service name for all exported spans. service-name: browser-js diff --git a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md index 69ebf2bc03..a9ef5ffc15 100644 --- a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md +++ b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md @@ -18,9 +18,9 @@ Due to the migration from OpenCensus to OpenTelemetry, the `OpenCensus Agent Exp Due to the migration to OpenTelemetry, InspectIT Ocelot now supports OpenTelemetry Protocol (OTLP) exporter for metrics and tracing. -#### Exporter property `url` replaced by `endpoint` +#### Exporter property `url` and `grpc` replaced by `endpoint` -Due to the migration to OpenTelemetry, we approach the naming of OpenTelemetry for the exporters' properties. For this, the previously used property `url` is replaced by the property `endpoint`. The deprecated property `url` is still supported in this release but will be removed in future releases. +Due to the migration to OpenTelemetry, we approach the naming of OpenTelemetry for the exporters' properties. For this, the previously used properties `url` and `grpc` are replaced by the property `endpoint`. The deprecated property `url` is still supported in this release but will be removed in future releases. #### New property `protocol` for Jaeger and OTLP exporter From d2eff036e838dadfa25162344c120b784e0506c9 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Wed, 13 Apr 2022 15:38:56 +0200 Subject: [PATCH 73/86] feat(config): [#1297] keep deprecated property `grpc` and automatically derive `protocol` from `url`/ `grpc` in the `JaegerExporterSettings` for this release --- .../model/exporters/TransportProtocol.java | 7 +++ .../trace/JaegerExporterSettings.java | 6 ++ .../ocelot/config/default/exporters.yml | 11 ++-- .../core/exporter/JaegerExporterService.java | 23 +++++--- .../JaegerExporterServiceIntTest.java | 58 +++++++++++++++++-- .../OtlpMetricsExporterServiceIntTest.java | 4 +- .../OtlpTraceExporterServiceIntTest.java | 10 ++-- .../docs/breaking-changes/breaking-changes.md | 2 +- 8 files changed, 95 insertions(+), 26 deletions(-) diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java index b2ef23ccd2..0418baa17f 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java @@ -26,6 +26,13 @@ public enum TransportProtocol { } } + /** + * Parses the given name into a {@link TransportProtocol} + * + * @param name The {@link TransportProtocol#name} + * + * @return The {@link TransportProtocol} with the given name + */ public static TransportProtocol parse(String name) { return names.containsKey(name) ? names.get(name) : TransportProtocol.valueOf(name); } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java index 9e75ffaca0..0df0cd3f9f 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/JaegerExporterSettings.java @@ -14,6 +14,12 @@ public class JaegerExporterSettings { */ private ExporterEnabledState enabled; + @Deprecated + /** + * This property is deprecated since v2.0. Please use {@link #endpoint} instead. + * The gRPC endpoint of the Jaeger server. + */ private String grpc; + @Deprecated /** * This property is deprecated since v2.0. Please use {@link #endpoint} instead. diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml index 4497d9c7b2..9b8a30c887 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/exporters.yml @@ -51,8 +51,8 @@ inspectit: export-interval: ${inspectit.metrics.frequency} # the gRPC API endpoint, e.g., http://127.0.0.1:4318 endpoint: null - # the transport protocol, e.g., grpc or http/protobuf - protocol: grpc + # the transport protocol, e.g., 'grpc' or 'http/protobuf' + protocol: null # settings for trace exporters tracing: @@ -70,8 +70,8 @@ inspectit: enabled: IF_CONFIGURED # the URL under which the jaeger thrift server can be accessed, e.g. http://127.0.0.1:14268/api/traces for http/thrift or http://127.0.0.1:14250/v1/traces for grpc endpoint: null - # the transport protocol - protocol: http/thrift + # the transport protocol, e.g., 'http/thrift' or 'grpc' + protocol: null # settings used in LoggingTraceExporterService for the LoggingSpanExporter (https://github.com/open-telemetry/opentelemetry-java/tree/main/exporters/logging) logging: @@ -82,4 +82,5 @@ inspectit: enabled: IF_CONFIGURED # the URL endpoint, e.g., http://127.0.0.1:4318 endpoint: null - protocol: grpc + # the transport protocol, e.g., 'http/thrift' or 'grpc' + protocol: null diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java index 36dc4f5302..fb8132aeda 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java @@ -42,11 +42,18 @@ protected void init() { protected boolean checkEnabledForConfig(InspectitConfig conf) { @Valid JaegerExporterSettings jaeger = conf.getExporters().getTracing().getJaeger(); if (conf.getTracing().isEnabled() && !jaeger.getEnabled().isDisabled()) { + + // fallback if 'protocol' was not set: derive from set properties 'url' or 'grpc' + if ((null == jaeger.getProtocol() || jaeger.getProtocol() + .equals(TransportProtocol.UNSET)) && (StringUtils.hasText(jaeger.getUrl()) || StringUtils.hasText(jaeger.getGrpc()))) { + jaeger.setProtocol(StringUtils.hasText(jaeger.getUrl()) ? TransportProtocol.HTTP_THRIFT : TransportProtocol.GRPC); + log.warn("The property 'protocol' was not set. Based on the set property '{}' we assume the protocol '{}'. This fallback will be removed in future releases. Please make sure to use the property 'protocol' in future.", StringUtils.hasText(jaeger.getUrl()) ? "url" : "grpc", StringUtils.hasText(jaeger.getUrl()) ? TransportProtocol.HTTP_THRIFT.getName() : TransportProtocol.GRPC.getName()); + } if (SUPPORTED_PROTOCOLS.contains(jaeger.getProtocol())) { if (StringUtils.hasText(jaeger.getEndpoint())) { return true; - } else if (StringUtils.hasText(jaeger.getUrl())) { - log.warn("You are using the deprecated property 'url'. This property will be invalid in future releases of InspectIT Ocelot, please use 'endpoint' instead."); + } else if (StringUtils.hasText(jaeger.getUrl()) || StringUtils.hasText(jaeger.getGrpc())) { + log.warn("You are using the deprecated property '{}'. This property will be invalid in future releases of InspectIT Ocelot, please use 'endpoint' instead.", StringUtils.hasText(jaeger.getUrl()) ? "url" : "grpc"); return true; } } @@ -56,7 +63,7 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { .map(transportProtocol -> transportProtocol.getName()) .toArray())); } - if (!StringUtils.hasText(jaeger.getEndpoint()) && !StringUtils.hasText(jaeger.getUrl())) { + if (!StringUtils.hasText(jaeger.getEndpoint()) && !StringUtils.hasText(jaeger.getUrl()) && !StringUtils.hasText(jaeger.getGrpc())) { log.warn("Jaeger Exporter is enabled but 'endpoint' is not set."); } } @@ -68,8 +75,8 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { protected boolean doEnable(InspectitConfig configuration) { try { JaegerExporterSettings settings = configuration.getExporters().getTracing().getJaeger(); - String endpoint = StringUtils.hasText(settings.getEndpoint()) ? settings.getEndpoint() : settings.getUrl(); - log.info("Starting Jaeger Thrift Exporter with endpoint '{}'", endpoint); + String endpoint = StringUtils.hasText(settings.getEndpoint()) ? settings.getEndpoint() : StringUtils.hasText(settings.getUrl()) ? settings.getUrl() : settings.getGrpc(); + log.info("Starting Jaeger Exporter with endpoint '{}' and protocol '{}'", endpoint, settings.getProtocol()); // create span exporter switch (settings.getProtocol()) { @@ -88,21 +95,21 @@ protected boolean doEnable(InspectitConfig configuration) { return true; } catch (Throwable t) { - log.error("Error creating Jaeger exporter", t); + log.error("Error creating Jaeger Exporter", t); return false; } } @Override protected boolean doDisable() { - log.info("Stopping Jaeger Thrift Exporter"); + log.info("Stopping Jaeger Exporter"); try { openTelemetryController.unregisterTraceExporterService(this); if (null != spanExporter) { spanExporter.close(); } } catch (Throwable t) { - log.error("Error disabling Jaeger Thrift Exporter", t); + log.error("Error disabling Jaeger Exporter", t); } return true; } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java index 76d8cb62f7..53cbffabd2 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java @@ -117,9 +117,6 @@ void verifyTraceSentGrpc() { makeSpansAndFlush("jaeger-grpc-parent", "jaeger-grpc-child"); awaitSpansExported("jaeger-grpc-parent", "jaeger-grpc-child"); - /* await().atMost(15, TimeUnit.SECONDS) - .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(grpcServer.traceRequests).hasSize(1));*/ } /** @@ -195,7 +192,11 @@ void testDefault() { .getTracing() .getJaeger() .getEndpoint()).isNullOrEmpty(); - // TODO: what is the default for protocol? + assertThat(environment.getCurrentConfig() + .getExporters() + .getTracing() + .getJaeger() + .getProtocol()).isEqualTo(TransportProtocol.UNSET); } @Test @@ -205,9 +206,54 @@ void defaultSettings() { JaegerExporterSettings jaeger = environment.getCurrentConfig().getExporters().getTracing().getJaeger(); // enabled property is set to IF_CONFIGURED - AssertionsForClassTypes.assertThat(jaeger.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); + assertThat(jaeger.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); // endpoint is null or empty - AssertionsForClassTypes.assertThat(jaeger.getEndpoint()).isNullOrEmpty(); + assertThat(jaeger.getEndpoint()).isNullOrEmpty(); + // protocol is unset + assertThat(jaeger.getProtocol()).isEqualTo(TransportProtocol.UNSET); + } + + /** + * Test the fallback mechanism if the deprecated property {@link JaegerExporterSettings#url} is set but no {@link JaegerExporterSettings#protocol} + */ + @Test + void testFallbackNoProtocolSetWithURL() { + updateProperties(mps -> { + mps.setProperty("inspectit.exporters.tracing.jaeger.protocol", ""); + mps.setProperty("inspectit.exporters.tracing.jaeger.endpoint", ""); + mps.setProperty("inspectit.exporters.tracing.jaeger.url", "http://127.0.0.1:14268/api/traces"); + }); + + assertThat(service.isEnabled()).isTrue(); + assertThat(warnLogs.assertContains("'protocol'")); + assertThat(environment.getCurrentConfig() + .getExporters() + .getTracing() + .getJaeger() + .getProtocol()).isEqualTo(TransportProtocol.HTTP_THRIFT); + + } + + /** + * Test the fallback mechanism if the deprecated property {@link JaegerExporterSettings#grpc} is set but no {@link JaegerExporterSettings#protocol} + */ + @Test + void testFallbackNoProtocolSetWithGRPC() { + updateProperties(mps -> { + mps.setProperty("inspectit.exporters.tracing.jaeger.protocol", ""); + mps.setProperty("inspectit.exporters.tracing.jaeger.endpoint", ""); + mps.setProperty("inspectit.exporters.tracing.jaeger.grpc", "http://127.0.0.1:14250/api/traces"); + }); + + assertThat(service.isEnabled()).isTrue(); + assertThat(warnLogs.assertContains("'protocol'")); + assertThat(environment.getCurrentConfig() + .getExporters() + .getTracing() + .getJaeger() + .getProtocol()).isEqualTo(TransportProtocol.GRPC); + } + } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java index 24e3503d5d..498031630b 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java @@ -115,7 +115,7 @@ void defaultSettings() { assertThat(otlp.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); // endpoint is null or empty assertThat(otlp.getEndpoint()).isNullOrEmpty(); - // protocol is GRPC - assertThat(otlp.getProtocol()).isEqualTo(TransportProtocol.GRPC); + // protocol is UNSET + assertThat(otlp.getProtocol()).isEqualTo(TransportProtocol.UNSET); } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java index 32d1e0506b..38b40cf066 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java @@ -97,11 +97,13 @@ void defaultSettings() { // service is not running AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); - OtlpTraceExporterSettings otlpGrpc = environment.getCurrentConfig().getExporters().getTracing().getOtlp(); + OtlpTraceExporterSettings otlp = environment.getCurrentConfig().getExporters().getTracing().getOtlp(); // enabled property is set to IF_CONFIGURED - AssertionsForClassTypes.assertThat(otlpGrpc.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); - // url is null or empty - AssertionsForClassTypes.assertThat(otlpGrpc.getUrl()).isNullOrEmpty(); + assertThat(otlp.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); + // endpoint is null or empty + assertThat(otlp.getEndpoint()).isNullOrEmpty(); + // protocol is unset + assertThat(otlp.getProtocol()).isEqualTo(TransportProtocol.UNSET); } } diff --git a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md index a9ef5ffc15..7ab3f08aeb 100644 --- a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md +++ b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md @@ -20,7 +20,7 @@ Due to the migration to OpenTelemetry, InspectIT Ocelot now supports OpenTelemet #### Exporter property `url` and `grpc` replaced by `endpoint` -Due to the migration to OpenTelemetry, we approach the naming of OpenTelemetry for the exporters' properties. For this, the previously used properties `url` and `grpc` are replaced by the property `endpoint`. The deprecated property `url` is still supported in this release but will be removed in future releases. +Due to the migration to OpenTelemetry, we approach the naming of OpenTelemetry for the exporters' properties. For this, the previously used properties `url` and `grpc` are replaced by the property `endpoint`. The deprecated properties `url` and `grpc` are still supported in this release but will be removed in future releases. #### New property `protocol` for Jaeger and OTLP exporter From b63b5299df20d169414cd0e77a85a5751f99f054 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 5 May 2022 15:17:38 +0200 Subject: [PATCH 74/86] fix(otel): implemented changes requested in the pull request --- .../inspectit/ocelot/agent/AgentMain.java | 2 - .../Log4J2TraceIdAutoInjectorTest.java | 4 - .../inspectit/ocelot/utils/TestUtils.java | 12 - .../IOpenTelemetryController.java | 36 +-- .../NoopOpenTelemetryController.java | 14 +- .../StringToTransportProtocolConverter.java | 3 +- .../model/exporters/TransportProtocol.java | 36 ++- .../exporters/TransportProtocolSubset.java | 32 +++ .../TransportProtocolSubsetValidator.java | 23 ++ .../metrics/OtlpMetricsExporterSettings.java | 6 - .../trace/LoggingTraceExporterSettings.java | 2 - .../trace/OtlpTraceExporterSettings.java | 7 - .../config/model/tracing/TracingSettings.java | 12 +- .../ocelot/config/default/basics.yml | 4 + .../BootstrapInitializerConfiguration.java | 3 +- ...allyActivatableMetricsExporterService.java | 2 +- ...icallyActivatableTraceExporterService.java | 33 --- .../core/exporter/JaegerExporterService.java | 22 +- .../LoggingMetricExporterService.java | 8 +- .../exporter/LoggingTraceExporterService.java | 16 +- .../exporter/OtlpMetricsExporterService.java | 18 +- .../exporter/OtlpTraceExporterService.java | 35 ++- .../exporter/PrometheusExporterService.java | 17 +- .../core/exporter/ZipkinExporterService.java | 17 +- .../instrumentation/context/ContextUtil.java | 9 - .../core/instrumentation/hook/MethodHook.java | 7 +- .../OpenTelemetryControllerImpl.java | 271 ++++++++---------- .../core/opentelemetry/OpenTelemetryImpl.java | 40 +-- .../InfluxExporterServiceIntTest.java | 3 +- .../JaegerExporterServiceIntTest.java | 47 +-- .../LoggingMetricsExporterServiceIntTest.java | 2 +- .../OtlpMetricsExporterServiceIntTest.java | 10 +- .../OtlpTraceExporterServiceIntTest.java | 5 - .../TransportProtocolSubsetValidatorTest.java | 78 +++++ .../ZipkinExporterServiceIntTest.java | 41 +++ .../OpenTelemetryControllerImplTest.java | 31 +- .../docs/breaking-changes/breaking-changes.md | 4 +- .../docs/tracing/tracing.md | 4 +- 38 files changed, 461 insertions(+), 455 deletions(-) create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubset.java create mode 100644 inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubsetValidator.java delete mode 100644 inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java create mode 100644 inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/TransportProtocolSubsetValidatorTest.java diff --git a/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java b/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java index d89648c99f..94c206f1d4 100644 --- a/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java +++ b/inspectit-ocelot-agent/src/main/java/rocks/inspectit/ocelot/agent/AgentMain.java @@ -1,7 +1,5 @@ package rocks.inspectit.ocelot.agent; -import lombok.AllArgsConstructor; -import lombok.Value; import rocks.inspectit.ocelot.bootstrap.AgentManager; import rocks.inspectit.ocelot.bootstrap.Instances; diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java index 9ec711105c..67c6798fb0 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/instrumentation/special/logging/Log4J2TraceIdAutoInjectorTest.java @@ -8,7 +8,6 @@ import org.apache.logging.log4j.message.MessageFactory; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.instrumentation.InstrumentationSysTestBase; import rocks.inspectit.ocelot.logging.Log4J2LoggingRecorder; import rocks.inspectit.ocelot.utils.TestUtils; @@ -33,15 +32,12 @@ public static void initializeOpenTelemetry() { @Test public void logStringAndTraceExists() { - System.out.println(String.format("OTEL=%s, tracer=%s", Instances.openTelemetryController.getClass() - .getSimpleName(), Tracing.getTracer().getClass().getSimpleName())); Log4J2LoggingRecorder.loggingEvents.clear(); String message = "This is a traced String in {}."; String traceId; try (Scope scope = Tracing.getTracer().spanBuilder("test").startScopedSpan()) { traceId = Tracing.getTracer().getCurrentSpan().getContext().getTraceId().toLowerBase16(); - LOGGER.info(message, "Log4J2"); } diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java index acc91e55a7..a254d235fb 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java @@ -21,7 +21,6 @@ import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.sdk.trace.samplers.Sampler; import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; -import org.awaitility.Awaitility; import org.awaitility.core.ConditionTimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -232,17 +231,6 @@ public static void waitForInstrumentationToComplete() { }); } - /** - * Waits for the initialization of {@link rocks.inspectit.ocelot.core.opentelemetry.OpenTelemetryControllerImpl}, which is then registered to {@link Instances#openTelemetryController} - */ - public static void waitForOpenTelemetryControllerInitialization() { - Awaitility.await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { - assertThat(NoopOpenTelemetryController.INSTANCE != Instances.openTelemetryController).isTrue(); - assertThat(Instances.openTelemetryController.isEnabled()).isTrue(); - assertThat(Instances.openTelemetryController.isConfigured()).isTrue(); - }); - } - public static Map getCurrentTagsAsMap() { Map result = new HashMap<>(); InternalUtils.getTags(Tags.getTagger().getCurrentTagContext()) diff --git a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java index 11c601ba25..3769aeee9f 100644 --- a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java +++ b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/IOpenTelemetryController.java @@ -6,34 +6,18 @@ public interface IOpenTelemetryController { /** - * Returns a completely noop {@link IOpenTelemetryController} + * Gets whether the {@link IOpenTelemetryController} is configured and active. * - * @return + * @return Whether the {@link IOpenTelemetryController} is configured and active. */ - static IOpenTelemetryController noop() { - return NoopOpenTelemetryController.INSTANCE; - } + boolean isActive(); /** - * Gets whether the {@link IOpenTelemetryController} is configured + * Gets whether the {@link IOpenTelemetryController} has been shut down. Once shut down, it cannot be restarted * - * @return Whether the {@link IOpenTelemetryController} is configured + * @return Whether the {@link IOpenTelemetryController} has been shut down. */ - boolean isConfigured(); - - /** - * Gets whether the {@link IOpenTelemetryController} is enabled - * - * @return Whether the {@link IOpenTelemetryController} is enabled - */ - boolean isEnabled(); - - /** - * Gets whether the {@link IOpenTelemetryController} has been stopped. Once stopped, it cannot be restarted - * - * @return Whether the {@link IOpenTelemetryController} has been stopped. - */ - boolean isStopped(); + boolean isShutdown(); /** * Starts the {@link IOpenTelemetryController} @@ -62,4 +46,12 @@ static IOpenTelemetryController noop() { * Notifies the {@link IOpenTelemetryController} that something in the {@link rocks.inspectit.ocelot.config.model.metrics.MetricsSettings} or in the {@link rocks.inspectit.ocelot.config.model.exporters.metrics.MetricsExportersSettings} changed */ void notifyMetricsSettingsChanged(); + + /** + * Returns the name of this {@link IOpenTelemetryController} + * @return The name of this {@link IOpenTelemetryController} + */ + default String getName(){ + return getClass().getSimpleName(); + } } diff --git a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java index 38343aff4d..a26d43f13c 100644 --- a/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java +++ b/inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/opentelemetry/NoopOpenTelemetryController.java @@ -1,26 +1,24 @@ package rocks.inspectit.ocelot.bootstrap.opentelemetry; +/** + * No-operations implementation of the {@link IOpenTelemetryController}. + */ public class NoopOpenTelemetryController implements IOpenTelemetryController { public static final NoopOpenTelemetryController INSTANCE = new NoopOpenTelemetryController(); @Override - public boolean isConfigured() { + public boolean isActive() { return false; } - - @Override - public boolean isEnabled() { - return true; - } - + @Override public boolean start() { return true; } @Override - public boolean isStopped() { + public boolean isShutdown() { return false; } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/conversion/StringToTransportProtocolConverter.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/conversion/StringToTransportProtocolConverter.java index 7d170d533e..f4aa6dffa7 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/conversion/StringToTransportProtocolConverter.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/conversion/StringToTransportProtocolConverter.java @@ -5,7 +5,8 @@ import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; /** - * A {@link Converter} to convert from the String-representation to a {@link TransportProtocol} + * A {@link Converter} to convert from the String-representation to a {@link TransportProtocol}. + * This custom {@link Converter} is needed as we are using slash ('/') in the values used in the configuration, e.g., 'http/thrift'. */ public class StringToTransportProtocolConverter implements Converter { diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java index 0418baa17f..fad1d61644 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocol.java @@ -7,33 +7,47 @@ /** * The transport protocols used by metrics and tracing exporters. + * As we adhere to the naming convention of {@link https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md OpenTelemetry SDK Autoconfigure}, which uses '/'' in the protocl values, + * e.g., 'http/thrift' or 'http/protobuf'. + * To use the same value convention in our configuration, we need the custom {@link rocks.inspectit.ocelot.config.conversion.StringToTransportProtocolConverter} and the custom {@link #parse(String)} method that uses the underlying {@link #configRepresentation}. */ public enum TransportProtocol { - UNKNOWN("unknown"), UNSET(""), GRPC("grpc"), HTTP_THRIFT("http/thrift"), HTTP_PROTOBUF("http/protobuf"), HTTP_JSON("http/json"), COUNT("cnt"); + UNSET(""), GRPC("grpc"), HTTP_THRIFT("http/thrift"), HTTP_PROTOBUF("http/protobuf"); + /** + * The representation of the {@link TransportProtocol} used in configuration files. + * We use this additional property to support the value convention of OpenTelemetry, see class comment. + */ @Getter - private final String name; + private final String configRepresentation; - TransportProtocol(String name) { - this.name = name; + TransportProtocol(String configVal) { + configRepresentation = configVal; } - static Map names = new HashMap<>(); + static Map values = new HashMap<>(); static { for (TransportProtocol tp : TransportProtocol.values()) { - names.put(tp.getName(), tp); + values.put(tp.getConfigRepresentation(), tp); } } /** - * Parses the given name into a {@link TransportProtocol} + * Parses the given config value representation into a {@link TransportProtocol} * - * @param name The {@link TransportProtocol#name} + * @param configVal The {@link TransportProtocol#configRepresentation} * - * @return The {@link TransportProtocol} with the given name + * @return The {@link TransportProtocol} with the given config value. */ - public static TransportProtocol parse(String name) { - return names.containsKey(name) ? names.get(name) : TransportProtocol.valueOf(name); + public static TransportProtocol parse(String configVal) { + return values.containsKey(configVal) ? values.get(configVal) : TransportProtocol.valueOf(configVal.toUpperCase()); + } + + @Override + public String toString() { + // Override the default toString() method to return the config string representation. + // This is needed for the TransportProtocolSubset and TransportProtocolSubsetValidator, which use the toStringMethod in the validation message. + return configRepresentation; } } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubset.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubset.java new file mode 100644 index 0000000000..bc9e8a7df1 --- /dev/null +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubset.java @@ -0,0 +1,32 @@ +package rocks.inspectit.ocelot.config.utils; + +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Validate that an {@link TransportProtocol} is in the supported array of {@link #anyOf()}. + * Will fail if a property is not in {@link #anyOf()}. + */ +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +@Documented +@Constraint(validatedBy = TransportProtocolSubsetValidator.class) +public @interface TransportProtocolSubset { + + TransportProtocol[] anyOf(); + + String message() default "must be any of {anyOf}"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} + diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubsetValidator.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubsetValidator.java new file mode 100644 index 0000000000..b21ae3751b --- /dev/null +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubsetValidator.java @@ -0,0 +1,23 @@ +package rocks.inspectit.ocelot.config.utils; + +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.Arrays; + +public class TransportProtocolSubsetValidator implements ConstraintValidator { + + private TransportProtocol[] subset; + + @Override + public void initialize(TransportProtocolSubset constraintAnnotation) { + subset = constraintAnnotation.anyOf(); + } + + @Override + public boolean isValid(Enum value, ConstraintValidatorContext context) { + return Arrays.asList(subset).contains(value); + } +} + diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpMetricsExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpMetricsExporterSettings.java index ef6b187944..5c2e98592f 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpMetricsExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/metrics/OtlpMetricsExporterSettings.java @@ -17,12 +17,6 @@ public class OtlpMetricsExporterSettings { private ExporterEnabledState enabled; - @Deprecated - /*** - * This property is deprecated since v2.0. Please use {@link #endpoint} instead. - * The OTLP metrics endpoint to connect to. - */ private String url; - /** * The OTLP metrics endpoint to connect to. */ diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java index c2a6334f72..715fb62228 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/LoggingTraceExporterSettings.java @@ -13,6 +13,4 @@ public class LoggingTraceExporterSettings { private ExporterEnabledState enabled; - private String serviceName; - } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java index 9ff9967f35..6f745744df 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/trace/OtlpTraceExporterSettings.java @@ -14,12 +14,6 @@ public class OtlpTraceExporterSettings { private ExporterEnabledState enabled; - @Deprecated - /*** - * This property is deprecated since v2.0. Please use {@link #endpoint} instead. - * The OTLP traces gRPC endpoint to connect to. - */ private String url; - /** * The OTLP traces endpoint to connect to. */ @@ -31,5 +25,4 @@ public class OtlpTraceExporterSettings { */ private TransportProtocol protocol; - private String serviceName; } diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java index 4a0f77e2e6..909bb826f2 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java @@ -60,19 +60,15 @@ public enum AddCommonTags { @NotNull private PropagationFormat propagationFormat = PropagationFormat.B3; - static final int DEFAULT_MAX_EXPORT_BATCH_SIZE = 512; - /** - * The maximum batch size for every span export. Default value is 512. + * The maximum batch size for every span export. */ @Positive - private int maxExportBatchSize = DEFAULT_MAX_EXPORT_BATCH_SIZE; - - static final long DEFAULT_SCHEDULE_DELAY_MILLIS = 5000; + private int maxExportBatchSize = 512; /** - * Delay interval between two consecutive exports in milliseconds. Default value is 5000ms. + * Delay interval between two consecutive exports in milliseconds.. */ @Positive - private long scheduleDelayMillis = DEFAULT_SCHEDULE_DELAY_MILLIS; + private long scheduleDelayMillis = 5000; } diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/basics.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/basics.yml index 394826f50a..5225e4da82 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/basics.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/basics.yml @@ -60,6 +60,10 @@ inspectit: prefix: "[TraceID: " # the suffix to use when injecting a trace id suffix: "]" + # the maximum batch size for every span export, i.e., the maximum number of spans exported by the used BatchSpanProcessor + max-export-batch-size: 512 + # the delay interval between two consecutive exports in milliseconds. + schedule-delay-millis: 5000 # general settings regarding metrics capturing metrics: diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/spring/BootstrapInitializerConfiguration.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/spring/BootstrapInitializerConfiguration.java index a0ab6fdca0..fa47d7c65a 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/spring/BootstrapInitializerConfiguration.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/config/spring/BootstrapInitializerConfiguration.java @@ -56,8 +56,7 @@ public LogTraceCorrelatorImpl getLogTraceCorrelator(MdcAccessManager mdcAccessMa } @Bean(OpenTelemetryControllerImpl.BEAN_NAME) - public OpenTelemetryControllerImpl getOpenTelemetryController(InspectitEnvironment environment) { - InspectitConfig configuration = environment.getCurrentConfig(); + public OpenTelemetryControllerImpl getOpenTelemetryController() { return new OpenTelemetryControllerImpl(); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricsExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricsExporterService.java index 9f57d0cb41..3a2377f5ac 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricsExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableMetricsExporterService.java @@ -1,7 +1,6 @@ package rocks.inspectit.ocelot.core.exporter; import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.export.MetricReader; import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.config.model.exporters.metrics.PrometheusExporterSettings; @@ -15,6 +14,7 @@ public abstract class DynamicallyActivatableMetricsExporterService extends Dynam /** * Gets a new {@link MetricReaderFactory} for this service. + * It is important that this method returns a new {@link MetricReaderFactory}, as when the previously used {@link MetricReaderFactory} is shut down during {@link SdkMeterProvider#shutdown()}, it cannot be re-enabled. * * @return A new {@link MetricReaderFactory} */ diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java deleted file mode 100644 index 44eca549d0..0000000000 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/DynamicallyActivatableTraceExporterService.java +++ /dev/null @@ -1,33 +0,0 @@ -package rocks.inspectit.ocelot.core.exporter; - -import io.opentelemetry.sdk.trace.export.SpanExporter; -import rocks.inspectit.ocelot.config.model.InspectitConfig; -import rocks.inspectit.ocelot.config.model.exporters.metrics.PrometheusExporterSettings; -import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; - -/** - * Base class for trace export services that can be dynamically enabled and disabled based on the {@link InspectitConfig}. - * This class extends {@link DynamicallyActivatableService} which handles the waiting for changes in the configuration. - */ -public abstract class DynamicallyActivatableTraceExporterService extends DynamicallyActivatableService { - - /** - * Gets the {@link SpanExporter} of this service to export {@link io.opentelemetry.sdk.trace.data.SpanData} to - * - * @return The {@link SpanExporter} of this service to export {@link io.opentelemetry.sdk.trace.data.SpanData} to - */ - public abstract SpanExporter getSpanExporter(); - - /** - * Constructor. - * - * @param configDependencies The list of configuration properties in camelCase this service depends on. - * For example "exporters.metrics.prometheus" specifies a dependency - * to {@link PrometheusExporterSettings} - * and all its children. - */ - public DynamicallyActivatableTraceExporterService(String... configDependencies) { - super(configDependencies); - } - -} diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java index fb8132aeda..cfdaeda608 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterService.java @@ -11,6 +11,7 @@ import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; import rocks.inspectit.ocelot.config.model.exporters.trace.JaegerExporterSettings; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; import javax.validation.Valid; import java.util.Arrays; @@ -22,7 +23,7 @@ */ @Component @Slf4j -public class JaegerExporterService extends DynamicallyActivatableTraceExporterService { +public class JaegerExporterService extends DynamicallyActivatableService { private final List SUPPORTED_PROTOCOLS = Arrays.asList(TransportProtocol.GRPC, TransportProtocol.HTTP_THRIFT); @@ -47,7 +48,7 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { if ((null == jaeger.getProtocol() || jaeger.getProtocol() .equals(TransportProtocol.UNSET)) && (StringUtils.hasText(jaeger.getUrl()) || StringUtils.hasText(jaeger.getGrpc()))) { jaeger.setProtocol(StringUtils.hasText(jaeger.getUrl()) ? TransportProtocol.HTTP_THRIFT : TransportProtocol.GRPC); - log.warn("The property 'protocol' was not set. Based on the set property '{}' we assume the protocol '{}'. This fallback will be removed in future releases. Please make sure to use the property 'protocol' in future.", StringUtils.hasText(jaeger.getUrl()) ? "url" : "grpc", StringUtils.hasText(jaeger.getUrl()) ? TransportProtocol.HTTP_THRIFT.getName() : TransportProtocol.GRPC.getName()); + log.warn("The property 'protocol' was not set. Based on the set property '{}' we assume the protocol '{}'. This fallback will be removed in future releases. Please make sure to use the property 'protocol' in future.", StringUtils.hasText(jaeger.getUrl()) ? "url" : "grpc", StringUtils.hasText(jaeger.getUrl()) ? TransportProtocol.HTTP_THRIFT.getConfigRepresentation() : TransportProtocol.GRPC.getConfigRepresentation()); } if (SUPPORTED_PROTOCOLS.contains(jaeger.getProtocol())) { if (StringUtils.hasText(jaeger.getEndpoint())) { @@ -60,7 +61,7 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { if (jaeger.getEnabled().equals(ExporterEnabledState.ENABLED)) { if (!SUPPORTED_PROTOCOLS.contains(jaeger.getProtocol())) { log.warn("Jaeger Exporter is enabled, but wrong 'protocol' is specified. Supported values are ", Arrays.toString(SUPPORTED_PROTOCOLS.stream() - .map(transportProtocol -> transportProtocol.getName()) + .map(transportProtocol -> transportProtocol.getConfigRepresentation()) .toArray())); } if (!StringUtils.hasText(jaeger.getEndpoint()) && !StringUtils.hasText(jaeger.getUrl()) && !StringUtils.hasText(jaeger.getGrpc())) { @@ -76,9 +77,7 @@ protected boolean doEnable(InspectitConfig configuration) { try { JaegerExporterSettings settings = configuration.getExporters().getTracing().getJaeger(); String endpoint = StringUtils.hasText(settings.getEndpoint()) ? settings.getEndpoint() : StringUtils.hasText(settings.getUrl()) ? settings.getUrl() : settings.getGrpc(); - log.info("Starting Jaeger Exporter with endpoint '{}' and protocol '{}'", endpoint, settings.getProtocol()); - // create span exporter switch (settings.getProtocol()) { case GRPC: { spanExporter = JaegerGrpcSpanExporter.builder().setEndpoint(endpoint).build(); @@ -90,10 +89,13 @@ protected boolean doEnable(InspectitConfig configuration) { } } - // register - openTelemetryController.registerTraceExporterService(this); - - return true; + boolean success = openTelemetryController.registerTraceExporterService(spanExporter, getName()); + if (success) { + log.info("Starting Jaeger Exporter with endpoint '{}' and protocol '{}'", endpoint, settings.getProtocol()); + } else { + log.error("Failed to register {} at the OpenTelemetry controller!", getName()); + } + return success; } catch (Throwable t) { log.error("Error creating Jaeger Exporter", t); return false; @@ -104,7 +106,7 @@ protected boolean doEnable(InspectitConfig configuration) { protected boolean doDisable() { log.info("Stopping Jaeger Exporter"); try { - openTelemetryController.unregisterTraceExporterService(this); + openTelemetryController.unregisterTraceExporterService(getName()); if (null != spanExporter) { spanExporter.close(); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java index d30ef83b77..7f0fdb2523 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricExporterService.java @@ -50,18 +50,16 @@ protected boolean checkEnabledForConfig(InspectitConfig configuration) { protected boolean doEnable(InspectitConfig configuration) { LoggingMetricsExporterSettings logging = configuration.getExporters().getMetrics().getLogging(); try { - // build and register the MeterProvider metricReaderBuilder = PeriodicMetricReader.builder(metricExporter).setInterval(logging.getExportInterval()); boolean success = openTelemetryController.registerMetricExporterService(this); if (success) { - log.info("Starting {}", getClass().getSimpleName()); + log.info("Starting {}", getName()); } else { - log.error("Failed to register {} at {}!", getClass().getSimpleName(), openTelemetryController.getClass() - .getSimpleName()); + log.error("Failed to register {} at the OpenTelemetry controller!", getName()); } return success; } catch (Exception e) { - log.error("Failed to start " + getClass().getSimpleName(), e); + log.error("Failed to start " + getName(), e); return false; } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java index 6b32ccee16..6ce6304d53 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/LoggingTraceExporterService.java @@ -6,6 +6,7 @@ import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.config.model.exporters.trace.LoggingTraceExporterSettings; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; import javax.validation.Valid; @@ -14,7 +15,7 @@ */ @Component @Slf4j -public class LoggingTraceExporterService extends DynamicallyActivatableTraceExporterService { +public class LoggingTraceExporterService extends DynamicallyActivatableService { /** * The {@link LoggingSpanExporter} for exporting the spans to the log @@ -43,19 +44,16 @@ protected boolean checkEnabledForConfig(InspectitConfig configuration) { @Override protected boolean doEnable(InspectitConfig conf) { - LoggingTraceExporterSettings logging = conf.getExporters().getTracing().getLogging(); try { - - boolean success = openTelemetryController.registerTraceExporterService(this); + boolean success = openTelemetryController.registerTraceExporterService(spanExporter, getName()); if (success) { - log.info("Starting {}", getClass().getSimpleName()); + log.info("Starting {}", getName()); } else { - log.error("Failed to register {} at {}!", getClass().getSimpleName(), openTelemetryController.getClass() - .getSimpleName()); + log.error("Failed to register {} at the OpenTelemetry controller!", getName()); } return success; } catch (Exception e) { - log.error("Failed to start " + getClass().getSimpleName(), e); + log.error("Failed to start " + getName(), e); return false; } } @@ -63,7 +61,7 @@ protected boolean doEnable(InspectitConfig conf) { @Override protected boolean doDisable() { try { - openTelemetryController.unregisterTraceExporterService(this); + openTelemetryController.unregisterTraceExporterService(getName()); if (null != spanExporter) { spanExporter.flush(); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterService.java index b7aec88e4b..b2fce008c3 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterService.java @@ -50,7 +50,7 @@ protected boolean checkEnabledForConfig(InspectitConfig configuration) { if (StringUtils.hasText(otlp.getEndpoint())) { return true; - } else if (StringUtils.hasText(otlp.getUrl())) { + } else if (StringUtils.hasText(otlp.getEndpoint())) { log.warn("You are using the deprecated property 'url'. This property will be invalid in future releases of InspectIT Ocelot, please use 'endpoint' instead."); return true; } @@ -58,31 +58,30 @@ protected boolean checkEnabledForConfig(InspectitConfig configuration) { if (otlp.getEnabled().equals(ExporterEnabledState.ENABLED)) { if (!SUPPORTED_PROTOCOLS.contains(otlp.getProtocol())) { log.warn("OTLP Metric Exporter is enabled, but wrong 'protocol' is specified. Supported values are ", Arrays.toString(SUPPORTED_PROTOCOLS.stream() - .map(transportProtocol -> transportProtocol.getName()) + .map(transportProtocol -> transportProtocol.getConfigRepresentation()) .toArray())); } - if (!StringUtils.hasText(otlp.getEndpoint()) && !StringUtils.hasText(otlp.getUrl())) { + if (!StringUtils.hasText(otlp.getEndpoint()) && !StringUtils.hasText(otlp.getEndpoint())) { log.warn("OTLP Metric Exporter is enabled but 'endpoint' is not set."); } } } + return false; } @Override protected boolean doEnable(InspectitConfig configuration) { try { - // build and register exporter service OtlpMetricsExporterSettings otlp = configuration.getExporters().getMetrics().getOtlp(); - String endpoint = StringUtils.hasText(otlp.getEndpoint()) ? otlp.getEndpoint() : otlp.getUrl(); switch (otlp.getProtocol()) { case GRPC: { - metricExporter = OtlpGrpcMetricExporter.builder().setEndpoint(endpoint).build(); + metricExporter = OtlpGrpcMetricExporter.builder().setEndpoint(otlp.getEndpoint()).build(); break; } case HTTP_PROTOBUF: { - metricExporter = OtlpHttpMetricExporter.builder().setEndpoint(endpoint).build(); + metricExporter = OtlpHttpMetricExporter.builder().setEndpoint(otlp.getEndpoint()).build(); break; } } @@ -90,10 +89,9 @@ protected boolean doEnable(InspectitConfig configuration) { boolean success = openTelemetryController.registerMetricExporterService(this); if (success) { - log.info("Starting {}", getClass().getSimpleName()); + log.info("Starting {}", getName()); } else { - log.error("Failed to register {} at {}!", getClass().getSimpleName(), openTelemetryController.getClass() - .getSimpleName()); + log.error("Failed to register {} at the OpenTelemetry controller!", getName()); } return success; } catch (Exception e) { diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java index 52111db13b..afce1a03d6 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterService.java @@ -11,6 +11,7 @@ import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; import rocks.inspectit.ocelot.config.model.exporters.trace.OtlpTraceExporterSettings; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; import javax.validation.Valid; import java.util.Arrays; @@ -22,7 +23,7 @@ */ @Component @Slf4j -public class OtlpTraceExporterService extends DynamicallyActivatableTraceExporterService { +public class OtlpTraceExporterService extends DynamicallyActivatableService { private final List SUPPORTED_PROTOCOLS = Arrays.asList(TransportProtocol.GRPC, TransportProtocol.HTTP_PROTOBUF); @@ -37,21 +38,16 @@ public OtlpTraceExporterService() { protected boolean checkEnabledForConfig(InspectitConfig configuration) { @Valid OtlpTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlp(); if (configuration.getTracing().isEnabled() && !otlp.getEnabled().isDisabled()) { - if (SUPPORTED_PROTOCOLS.contains(otlp.getProtocol())) { - if (StringUtils.hasText(otlp.getEndpoint())) { - return true; - } else if (StringUtils.hasText(otlp.getUrl())) { - log.warn("You are using the deprecated property 'url'. This property will be invalid in future releases of InspectIT Ocelot, please use 'endpoint' instead."); - return true; - } + if (SUPPORTED_PROTOCOLS.contains(otlp.getProtocol()) && StringUtils.hasText(otlp.getEndpoint())) { + return true; } if (otlp.getEnabled().equals(ExporterEnabledState.ENABLED)) { if (!SUPPORTED_PROTOCOLS.contains(otlp.getProtocol())) { log.warn("OTLP Trace Exporter is enabled, but wrong 'protocol' is specified. Supported values are ", Arrays.toString(SUPPORTED_PROTOCOLS.stream() - .map(transportProtocol -> transportProtocol.getName()) + .map(transportProtocol -> transportProtocol.getConfigRepresentation()) .toArray())); } - if (!StringUtils.hasText(otlp.getEndpoint()) && !StringUtils.hasText(otlp.getUrl())) { + if (!StringUtils.hasText(otlp.getEndpoint())) { log.warn("OTLP Trace Exporter is enabled but 'endpoint' is not set."); } } @@ -63,24 +59,25 @@ protected boolean checkEnabledForConfig(InspectitConfig configuration) { protected boolean doEnable(InspectitConfig configuration) { try { OtlpTraceExporterSettings otlp = configuration.getExporters().getTracing().getOtlp(); - String endpoint = StringUtils.hasText(otlp.getEndpoint()) ? otlp.getEndpoint() : otlp.getUrl(); - log.info("Starting OTLP Trace Exporter with endpoint {}", endpoint); - // create span exporter switch (otlp.getProtocol()) { case GRPC: { - spanExporter = OtlpGrpcSpanExporter.builder().setEndpoint(endpoint).build(); + spanExporter = OtlpGrpcSpanExporter.builder().setEndpoint(otlp.getEndpoint()).build(); break; } case HTTP_PROTOBUF: { - spanExporter = OtlpHttpSpanExporter.builder().setEndpoint(endpoint).build(); + spanExporter = OtlpHttpSpanExporter.builder().setEndpoint(otlp.getEndpoint()).build(); break; } } - // register service - openTelemetryController.registerTraceExporterService(this); - return true; + boolean success = openTelemetryController.registerTraceExporterService(spanExporter, getName()); + if (success) { + log.info("Starting OTLP Trace Exporter with endpoint {}", otlp.getEndpoint()); + } else { + log.error("Failed to register {} at the OpenTelemetry controller!", getName()); + } + return success; } catch (Throwable t) { log.error("Error creating OTLP Trace Exporter", t); return false; @@ -93,7 +90,7 @@ protected boolean doDisable() { log.info("Stopping OTLP Trace Exporter"); try { // unregister service - openTelemetryController.unregisterTraceExporterService(this); + openTelemetryController.unregisterTraceExporterService(getName()); if (null != spanExporter) { spanExporter.close(); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java index 28ac3f5607..9ff043f23f 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/PrometheusExporterService.java @@ -4,9 +4,9 @@ import io.opentelemetry.exporter.prometheus.PrometheusHttpServerBuilder; import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; import lombok.extern.slf4j.Slf4j; -import lombok.val; import org.springframework.stereotype.Component; import rocks.inspectit.ocelot.config.model.InspectitConfig; +import rocks.inspectit.ocelot.config.model.exporters.metrics.PrometheusExporterSettings; /** * Service for the Prometheus OpenTelemetry exporter. @@ -27,24 +27,29 @@ protected boolean checkEnabledForConfig(InspectitConfig conf) { return conf.getMetrics().isEnabled() && !conf.getExporters() .getMetrics() .getPrometheus() - .getEnabled().isDisabled(); + .getEnabled() + .isDisabled(); } @Override protected boolean doEnable(InspectitConfig configuration) { - val config = configuration.getExporters().getMetrics().getPrometheus(); + PrometheusExporterSettings config = configuration.getExporters().getMetrics().getPrometheus(); try { String host = config.getHost(); int port = config.getPort(); - log.info("Starting Prometheus Exporter on {}:{}", host, port); prometheusHttpServerBuilder = PrometheusHttpServer.builder().setHost(host).setPort(port); - openTelemetryController.registerMetricExporterService(this); + boolean success = openTelemetryController.registerMetricExporterService(this); + if (success) { + log.info("Starting Prometheus Exporter on {}:{}", host, port); + } else { + log.error("Failed to register {} at the OpenTelemetry controller!", getName()); + } + return success; } catch (Exception e) { log.error("Error Starting Prometheus HTTP Endpoint!", e); return false; } - return true; } @Override diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java index ac73946737..f9426e30b6 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterService.java @@ -8,6 +8,7 @@ import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; import rocks.inspectit.ocelot.config.model.exporters.trace.ZipkinExporterSettings; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; import javax.validation.Valid; @@ -17,7 +18,7 @@ */ @Component @Slf4j -public class ZipkinExporterService extends DynamicallyActivatableTraceExporterService { +public class ZipkinExporterService extends DynamicallyActivatableService { @Getter private ZipkinSpanExporter spanExporter; @@ -47,14 +48,16 @@ protected boolean doEnable(InspectitConfig configuration) { try { ZipkinExporterSettings settings = configuration.getExporters().getTracing().getZipkin(); String endpoint = StringUtils.hasText(settings.getEndpoint()) ? settings.getEndpoint() : settings.getUrl(); - log.info("Starting Zipkin Exporter with endpoint '{}'", endpoint); - // create span exporter spanExporter = ZipkinSpanExporter.builder().setEndpoint(endpoint).build(); - // register - openTelemetryController.registerTraceExporterService(this); - return true; + boolean success = openTelemetryController.registerTraceExporterService(spanExporter, getName()); + if (success) { + log.info("Starting Zipkin Exporter with endpoint '{}'", endpoint); + } else { + log.error("Failed to register {} at the OpenTelemetry controller!", getName()); + } + return success; } catch (Throwable t) { log.error("Error creating Zipkin exporter", t); return false; @@ -65,7 +68,7 @@ protected boolean doEnable(InspectitConfig configuration) { protected boolean doDisable() { log.info("Stopping Zipkin Exporter"); try { - openTelemetryController.unregisterTraceExporterService(this); + openTelemetryController.unregisterTraceExporterService(getName()); if (null != spanExporter) { spanExporter.close(); } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextUtil.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextUtil.java index d3be58c6e4..113c721327 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextUtil.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextUtil.java @@ -38,15 +38,6 @@ public static InspectitContextImpl currentInspectitContext() { return current().get(InspectitContextImpl.INSPECTIT_KEY); } - /** - * Gets the {@link InspectitContext} stored in the {@link #currentGrpc() current GRPC context} with the {@link InspectitContextImpl#INSPECTIT_KEY_GRPC} - * - * @return The {@link InspectitContext} stored in the {@link #currentGrpc() current GRPC context} with the {@link InspectitContextImpl#INSPECTIT_KEY_GRPC} - */ - public static InspectitContextImpl currentInspectitContextStoredInGrpcContext() { - return InspectitContextImpl.INSPECTIT_KEY_GRPC.get(currentGrpc()); - } - /** * Gets the object stored in {@link #current()} with the given {@link ContextKey} * diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java index 30f814457c..bdd2c45bd5 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/hook/MethodHook.java @@ -13,7 +13,6 @@ import rocks.inspectit.ocelot.core.selfmonitoring.ActionScopeFactory; import rocks.inspectit.ocelot.core.selfmonitoring.IActionScope; -import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -98,8 +97,7 @@ public InternalInspectitContext onEnter(Object[] args, Object thiz) { try (IActionScope scope = actionScopeFactory.createScope(action)) { action.execute(executionContext); } catch (Throwable t) { - log.error("Entry action {} executed for method {} threw an exception and from now on is disabled!", action, methodInformation - .getMethodFQN(), t); + log.error("Entry action {} executed for method {} threw an exception and from now on is disabled!", action, methodInformation.getMethodFQN(), t); activeEntryActions.remove(action); } } @@ -122,8 +120,7 @@ public void onExit(Object[] args, Object thiz, Object returnValue, Throwable thr try (IActionScope scope = actionScopeFactory.createScope(action)) { action.execute(executionContext); } catch (Throwable t) { - log.error("Exit action {} executed for method {} threw an exception and from now on is disabled!", action, methodInformation - .getMethodFQN(), t); + log.error("Exit action {} executed for method {} threw an exception and from now on is disabled!", action, methodInformation.getMethodFQN(), t); activeExitActions.remove(action); } } diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java index beca721c3f..7e9bd3f09d 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java @@ -12,6 +12,7 @@ import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import lombok.AccessLevel; import lombok.Getter; @@ -28,7 +29,6 @@ import rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService; -import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableTraceExporterService; import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; @@ -40,7 +40,7 @@ /** * The implementation of {@link IOpenTelemetryController}. The {@link OpenTelemetryControllerImpl} configures {@link GlobalOpenTelemetry}. - * The individual {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService services}, i.e., {@link DynamicallyActivatableMetricsExporterService} and {@link DynamicallyActivatableTraceExporterService}, register to and unregister from {@link OpenTelemetryControllerImpl this}. + * The individual {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService services} register to and unregister from {@link OpenTelemetryControllerImpl this}. * Important note: {@link #shutdown() shutting down} the {@link OpenTelemetryControllerImpl} is final and cannot be revoked. */ @Slf4j @@ -48,11 +48,11 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { public static final String BEAN_NAME = "openTelemetryController"; + /** + * Whether this {@link OpenTelemetryControllerImpl} has been shut down. + */ @Getter - private boolean enabled = false; - - @Getter - private boolean stopped = false; + private boolean shutdown = false; /** * Whether something in {@link rocks.inspectit.ocelot.config.model.tracing.TracingSettings} or any of the {@link rocks.inspectit.ocelot.config.model.exporters.trace.TraceExportersSettings} of the {@link InspectitConfig} changed @@ -65,13 +65,13 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { private boolean metricSettingsChanged = false; /** - * whether {@link GlobalOpenTelemetry} has been successfully been configured. + * whether {@link GlobalOpenTelemetry} has been successfully been configured and is active. */ @Getter - private boolean configured = false; + private boolean active = false; /** - * Whether the {@link OpenTelemetryControllerImpl} is currently configuring and starting + * Whether the {@link OpenTelemetryControllerImpl} is currently configuring and starting. */ private AtomicBoolean isConfiguring = new AtomicBoolean(false); @@ -81,11 +81,11 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { private AtomicBoolean isShuttingDown = new AtomicBoolean(false); /** - * The registered {@link DynamicallyActivatableTraceExporterService}. + * The registered {@link SpanExporter} of a {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService trace exporter service}. */ @VisibleForTesting @Getter(AccessLevel.PACKAGE) - Map registeredTraceExportServices = new ConcurrentHashMap<>(); + Map registeredTraceExportServices = new ConcurrentHashMap<>(); /** * The registered {@link DynamicallyActivatableMetricsExporterService}. @@ -131,11 +131,11 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController { private SpanProcessor spanProcessor; /** - * The {@link DynamicMultiSpanExporter} wrapper that is used to forward all spans to a list of {@link io.opentelemetry.sdk.trace.export.SpanExporter} (one for each {@link DynamicallyActivatableTraceExporterService} + * The {@link DynamicMultiSpanExporter} wrapper that is used to forward all spans to a list of {@link io.opentelemetry.sdk.trace.export.SpanExporter} (one for each {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService trace exporter service} */ @VisibleForTesting @Setter(AccessLevel.PACKAGE) - private DynamicMultiSpanExporter spanExporter; + private DynamicMultiSpanExporter multiSpanExporter; @PostConstruct @VisibleForTesting @@ -145,40 +145,28 @@ void init() { // close the tracer provider when the JVM is shutting down Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); - enabled = true; - Instances.openTelemetryController = this; } @EventListener(ContextRefreshedEvent.class) @Order(Ordered.LOWEST_PRECEDENCE) synchronized private void startAtStartup(ContextRefreshedEvent event) { - // start and configure OTEL at when the ApplicationContext gets initialized start(); } - /** - * Returns whether the {@link OpenTelemetryControllerImpl} is currently (re-)configuring tracing and metrics - * - * @return Whether the {@link OpenTelemetryControllerImpl} is currently (re-)configuring tracing and metrics - */ - public boolean isConfiguring() { - return isConfiguring.get(); - } - /** * Configures and registers {@link io.opentelemetry.api.OpenTelemetry}, triggered by the {@link rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent} triggered * For tracing, the {@link SdkTracerProvider} is reconfigured and updated in the {@link GlobalOpenTelemetry}. - * For metrics, the {@link SdkMeterProvider} is reconfigured and updated in the {@link GlobalOpenTelemetry} + * For metrics, the {@link SdkMeterProvider} is reconfigured and updated in the {@link GlobalOpenTelemetry}. * + * Using the {@link Order} annotation, we make sure this method called after the individual services have (un)-registered. * * @return */ @EventListener(InspectitConfigChangedEvent.class) @Order(Ordered.LOWEST_PRECEDENCE) @VisibleForTesting - // make sure this is called after the individual services have (un)-registered synchronized boolean configureOpenTelemetry() { - if (stopped) { + if (shutdown) { return false; } boolean success = true; @@ -186,52 +174,44 @@ synchronized boolean configureOpenTelemetry() { log.info("Multiple configure calls"); return true; } - if (enabled) { - - InspectitConfig configuration = env.getCurrentConfig(); - // set serviceName - serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, configuration.getServiceName())); + InspectitConfig configuration = env.getCurrentConfig(); - // check if the tracing sample probability changed - if (null == sampler || sampler.getSampleProbability() != configuration.getTracing() - .getSampleProbability()) { - tracingSettingsChanged = true; - } + serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, configuration.getServiceName())); - if (!configured || metricSettingsChanged || tracingSettingsChanged) { + // check if the tracing sample probability changed + if (null == sampler || sampler.getSampleProbability() != configuration.getTracing().getSampleProbability()) { + tracingSettingsChanged = true; + } - // configure tracing if not configured or when tracing settings changed - SdkTracerProvider sdkTracerProvider = !(tracingSettingsChanged || !configured) ? tracerProvider : configureTracing(configuration); + if (!active || metricSettingsChanged || tracingSettingsChanged) { - // configure meter provider (metrics) if not configured or when metrics settings changed - SdkMeterProvider sdkMeterProvider = !(metricSettingsChanged || !configured) ? meterProvider : configureMeterProvider(configuration); + // configure tracing if not configured or when tracing settings changed + SdkTracerProvider sdkTracerProvider = !(tracingSettingsChanged || !active) ? tracerProvider : configureTracing(configuration); - // only if metrics settings changed or OTEL has not been configured before, we need to rebuild the OpenTelemetrySdk - if (metricSettingsChanged || !configured) { - OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() - .setTracerProvider(sdkTracerProvider) - .setMeterProvider(sdkMeterProvider) - .build(); - // update OTEL - openTelemetry.set(openTelemetrySdk, false, false); - } - success = null != sdkMeterProvider && null != sdkTracerProvider; - // update meterProvider and tracerProvider - meterProvider = sdkMeterProvider; - tracerProvider = sdkTracerProvider; - } + // configure meter provider (metrics) if not configured or when metrics settings changed + SdkMeterProvider sdkMeterProvider = !(metricSettingsChanged || !active) ? meterProvider : configureMeterProvider(); - if (success) { - log.info("Successfully configured OpenTelemetry with tracing and metrics"); - } else { - log.error("Failed to configure OpenTelemetry. Please scan the logs for detailed failure messages."); + // only if metrics settings changed or OTEL has not been configured and is running, we need to rebuild the OpenTelemetrySdk + if (metricSettingsChanged || !active) { + OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() + .setTracerProvider(sdkTracerProvider) + .setMeterProvider(sdkMeterProvider) + .build(); + openTelemetry.set(openTelemetrySdk, false, false); } + success = null != sdkMeterProvider && null != sdkTracerProvider; + meterProvider = sdkMeterProvider; + tracerProvider = sdkTracerProvider; + } + if (success) { + log.info("Successfully configured OpenTelemetry with tracing and metrics"); + } else { + log.error("Failed to configure OpenTelemetry. Please scan the logs for detailed failure messages."); } isConfiguring.set(false); - // reset changed variables tracingSettingsChanged = false; metricSettingsChanged = false; return success; @@ -239,13 +219,17 @@ synchronized boolean configureOpenTelemetry() { @Override synchronized public boolean start() { - enabled = true; - // if OTEL has not been configured (since last shutdown), configure it - return configured = !configured ? configureOpenTelemetry() : true; + // if OTEL is not already up and running, configure and start it + if (active) { + throw new IllegalStateException("The OpenTelemetry controller is already running and cannot be started again."); + } else { + active = configureOpenTelemetry(); + return active; + } } /** - * Flushes the all pending spans ({@link #openTelemetry}) and metrics ({@link #meterProvider}) and waits for it to complete. + * Flushes all pending spans ({@link #openTelemetry}) and metrics ({@link #meterProvider}) and waits for it to complete. */ @Override public void flush() { @@ -254,17 +238,18 @@ public void flush() { /** * Shuts down the {@link OpenTelemetryControllerImpl} by calling {@link OpenTelemetryImpl#close()} and waits for it to complete. - * The shutdown is final, i.e., once this {@link OpenTelemetryImpl} is shutdown, it cannot be re-enabled! + * The shutdown is final, i.e., once this {@link OpenTelemetryImpl} is shutdown, it cannot be restarted! *

* Only use this method for testing or when the JVM is shutting down. */ @Override synchronized public void shutdown() { - if (isStopped()) { + if (isShutdown()) { return; } if (!isShuttingDown.compareAndSet(false, true)) { log.info("Multiple shutdown calls"); + return; } long start = System.nanoTime(); @@ -277,9 +262,8 @@ synchronized public void shutdown() { } GlobalOpenTelemetry.resetForTest(); - configured = false; - enabled = false; - stopped = true; + active = false; + shutdown = true; isShuttingDown.set(false); // set all OTEL related fields to null @@ -287,7 +271,7 @@ synchronized public void shutdown() { meterProvider = null; serviceNameResource = null; sampler = null; - spanExporter = null; + multiSpanExporter = null; spanProcessor = null; log.info("Shut down {}. The shutdown process took {} ms", getClass().getSimpleName(), (System.nanoTime() - start) / 1000000); @@ -304,49 +288,31 @@ synchronized public void notifyMetricsSettingsChanged() { } /** - * Initializes tracer and meter provider components, i.e., {@link #openTelemetry}, {@link #spanExporter}, {@link #spanProcessor}, {@link #sampler}, {@link SdkTracerProvider}, and {@link SdkMeterProvider} + * Initializes tracer and meter provider components, i.e., {@link #openTelemetry}, {@link #multiSpanExporter}, {@link #spanProcessor}, {@link #sampler}, {@link SdkTracerProvider}, and {@link SdkMeterProvider} * * @param configuration */ @VisibleForTesting void initOtel(InspectitConfig configuration) { - // create the service name resource serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, env.getCurrentConfig() .getServiceName())); - - // build new SdkTracerProvider - double sampleProbability = configuration.getTracing().getSampleProbability(); - // set up sampler - sampler = new DynamicSampler(sampleProbability); - // set up spanProcessor and spanExporter - spanExporter = DynamicMultiSpanExporter.create(); - spanProcessor = BatchSpanProcessor.builder(spanExporter) - .setMaxExportBatchSize(configuration.getTracing().getMaxExportBatchSize()) - .setScheduleDelay(configuration.getTracing().getScheduleDelayMillis(), TimeUnit.MILLISECONDS) - .build(); - // build TracerProvider - SdkTracerProviderBuilder builder = SdkTracerProvider.builder() - .setSampler(sampler) - .setResource(serviceNameResource) - .addSpanProcessor(spanProcessor); - tracerProvider = builder.build(); - - // build new SdkMeterProvider + tracerProvider = buildTracerProvider(configuration); meterProvider = SdkMeterProvider.builder().setResource(serviceNameResource).build(); - // build OTEL OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) .setMeterProvider(meterProvider) .build(); - // build and register OpenTelemetryImpl openTelemetry = OpenTelemetryImpl.builder().openTelemetry(openTelemetrySdk).build(); - // check if any OpenTelemetry has been registered to GlobalOpenTelemetry. - // If so, reset it. + + // if any OpenTelemetry has already been registered to GlobalOpenTelemetry, reset it. if (null != OpenTelemetryUtils.getGlobalOpenTelemetry()) { - log.info("reset {}", GlobalOpenTelemetry.get().getClass().getName()); + // we need to reset it before we can register our custom OpenTelemetryImpl, as GlobalOpenTelemetry is throwing an exception if we want to register a new OpenTelemetry if a previous one is still registered. + log.info("Reset previously registered GlobalOpenTelemetry ({}) during the initialization of {} to register {}", GlobalOpenTelemetry.get() + .getClass() + .getName(), getName(), openTelemetry.getClass().getSimpleName()); GlobalOpenTelemetry.resetForTest(); } openTelemetry.registerGlobal(); @@ -356,19 +322,40 @@ void initOtel(InspectitConfig configuration) { } /** - * Configures the tracing, i.e. {@link #openTelemetry} and the related {@link SdkTracerProvider}. A new {@link SdkTracerProvider} is only built once or after {@link #shutdown()} was called. + * Builds a new {@link SdkTracerProvider} based on the given {@link InspectitConfig} * * @param configuration * + * @return A new {@link SdkTracerProvider} based on the {@link InspectitConfig} + */ + private SdkTracerProvider buildTracerProvider(InspectitConfig configuration) { + double sampleProbability = configuration.getTracing().getSampleProbability(); + sampler = new DynamicSampler(sampleProbability); + multiSpanExporter = DynamicMultiSpanExporter.create(); + spanProcessor = BatchSpanProcessor.builder(multiSpanExporter) + .setMaxExportBatchSize(configuration.getTracing().getMaxExportBatchSize()) + .setScheduleDelay(configuration.getTracing().getScheduleDelayMillis(), TimeUnit.MILLISECONDS) + .build(); + SdkTracerProviderBuilder builder = SdkTracerProvider.builder() + .setSampler(sampler) + .setResource(serviceNameResource) + .addSpanProcessor(spanProcessor); + return builder.build(); + } + + /** + * Configures the tracing, i.e. {@link #openTelemetry} and the related {@link SdkTracerProvider}. A new {@link SdkTracerProvider} is only built once or after {@link #shutdown()} was called. + * + * @param configuration The {@link InspectitConfig} used to build the {@link SdkTracerProvider} + * * @return The updated {@link SdkTracerProvider} or null if the configuration failed. */ @VisibleForTesting synchronized SdkTracerProvider configureTracing(InspectitConfig configuration) { - if (!enabled || stopped) { + if (shutdown) { return null; } try { - // update sample probability sampler.setSampleProbability(configuration.getTracing().getSampleProbability()); return tracerProvider; @@ -381,24 +368,19 @@ synchronized SdkTracerProvider configureTracing(InspectitConfig configuration) { /** * Configures the {@link SdkMeterProvider} * - * @param configuration - * * @return The updated {@link SdkMeterProvider} or null if the configuration failed. */ @VisibleForTesting - synchronized SdkMeterProvider configureMeterProvider(InspectitConfig configuration) { - if (!enabled || stopped) { + synchronized SdkMeterProvider configureMeterProvider() { + if (shutdown) { return null; } try { // stop the previously registered MeterProvider if (null != meterProvider) { - long start = System.nanoTime(); OpenTelemetryUtils.stopMeterProvider(meterProvider, true); - // log.info("time to stopMeterProvider: {} ms", (System.nanoTime() - start) / 1000000); } - // build new SdkMeterProvider SdkMeterProviderBuilder builder = SdkMeterProvider.builder().setResource(serviceNameResource); // register metric reader for each service @@ -406,9 +388,7 @@ synchronized SdkMeterProvider configureMeterProvider(InspectitConfig configurati builder.registerMetricReader(OpenCensusMetrics.attachTo(metricsExportService.getNewMetricReaderFactory())); } - SdkMeterProvider sdkMeterProvider = builder.build(); - - return sdkMeterProvider; + return builder.build(); } catch (Exception e) { log.error("Failed to configure MeterProvider", e); @@ -417,56 +397,51 @@ synchronized SdkMeterProvider configureMeterProvider(InspectitConfig configurati } /** - * Registers a new {@link DynamicallyActivatableTraceExporterService} that is used to export {@link io.opentelemetry.sdk.trace.data.SpanData} for sampled {@link io.opentelemetry.api.trace.Span}s + * Registers a new {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService trace exporter service} that is used to export {@link io.opentelemetry.sdk.trace.data.SpanData} for sampled {@link io.opentelemetry.api.trace.Span}s * - * @param service + * @param spanExporter The {@link SpanExporter} of the {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService trace exporter service} + * @param serviceName The name of the trace exporter service * - * @return + * @return Whether the registration was successful */ - public boolean registerTraceExporterService(DynamicallyActivatableTraceExporterService service) { - if (null == registeredTraceExportServices) { - registeredTraceExportServices = new ConcurrentHashMap<>(); - } + public boolean registerTraceExporterService(SpanExporter spanExporter, String serviceName) { try { // try to register the span exporter of the service - if (null != spanExporter) { - if (spanExporter.registerSpanExporter(service.getName(), service.getSpanExporter())) { - log.info("The spanExporter {} for the service {} was successfully registered.", service.getSpanExporter() - .getClass() - .getName(), service.getName()); + if (null != multiSpanExporter) { + if (multiSpanExporter.registerSpanExporter(serviceName, spanExporter)) { + log.info("The spanExporter {} for the service {} was successfully registered.", spanExporter.getClass() + .getName(), serviceName); } else { - log.error("The spanExporter {} for the service {} was already registered", service.getSpanExporter() - .getClass() - .getName(), service.getName()); + log.error("The spanExporter {} for the service {} was already registered", spanExporter.getClass() + .getName(), serviceName); } } // try to add the service if it has not already been registered - if (null == registeredTraceExportServices.put(service.getName(), service)) { + if (null == registeredTraceExportServices.put(serviceName, spanExporter)) { notifyTracingSettingsChanged(); - log.info("The service {} was successfully registered.", service.getName()); + log.info("The service {} was successfully registered.", serviceName); return true; } else { - log.warn("The service {} was already registered", service.getName()); + log.warn("The service {} was already registered", serviceName); return false; } } catch (Exception e) { - log.error("Failed to register " + service.getName(), e); + log.error("Failed to register " + serviceName, e); return false; } } /** - * Unregisters a {@link DynamicallyActivatableTraceExporterService} registered under the given name + * Unregisters a {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService trace exporter service} registered under the given name. + * For this, the {@link SpanExporter} of the {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService} is removed from {@link #registeredTraceExportServices} and {@link #multiSpanExporter}. * - * @param serviceName The name of the {@link DynamicallyActivatableTraceExporterService service} + * @param serviceName The name of the {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService trace exporter service} * - * @return Whether the {@link DynamicallyActivatableTraceExporterService service} was successfully unregistered. Returns false if no service with the given name was previously registered. + * @return Whether the {@link rocks.inspectit.ocelot.core.service.DynamicallyActivatableService trace exporter service} was successfully unregistered. Returns false if no service with the given name was previously registered */ - private boolean unregisterTraceExporterService(String serviceName) { - // unregister the service by removing it from the map of registered services and from the spanExporter - // evaluates to true when a service with the given name was previously registered - if (null != registeredTraceExportServices.remove(serviceName) & (spanExporter == null || spanExporter.unregisterSpanExporter(serviceName))) { + public boolean unregisterTraceExporterService(String serviceName) { + if (null != registeredTraceExportServices.remove(serviceName) & (multiSpanExporter == null || multiSpanExporter.unregisterSpanExporter(serviceName))) { notifyTracingSettingsChanged(); return true; } else { @@ -475,28 +450,14 @@ private boolean unregisterTraceExporterService(String serviceName) { } } - /** - * Unregisters a {@link DynamicallyActivatableTraceExporterService} - * - * @param service The {@link DynamicallyActivatableTraceExporterService} - * - * @return Whether the {@link DynamicallyActivatableTraceExporterService service} was successfully unregistered. Returns false if no service with the given name was previously registered. - */ - public boolean unregisterTraceExporterService(DynamicallyActivatableTraceExporterService service) { - return unregisterTraceExporterService(service.getName()); - } - /** * Registers a {@link DynamicallyActivatableMetricsExporterService} * - * @param service + * @param service The {@link DynamicallyActivatableMetricsExporterService} to register * - * @return + * @return Whether the {@link DynamicallyActivatableMetricsExporterService} was successfully registered */ public boolean registerMetricExporterService(DynamicallyActivatableMetricsExporterService service) { - if (null == registeredMetricExporterServices) { - registeredMetricExporterServices = new ConcurrentHashMap<>(); - } try { if (null == registeredMetricExporterServices.put(service.getName(), service)) { notifyMetricsSettingsChanged(); @@ -513,9 +474,9 @@ public boolean registerMetricExporterService(DynamicallyActivatableMetricsExport } /** - * Unregisters a {@link DynamicallyActivatableMetricsExporterService} with the given name + * Unregisters a {@link DynamicallyActivatableMetricsExporterService} with the given name. * - * @param serviceName The name of the {@link DynamicallyActivatableTraceExporterService service} + * @param serviceName The name of the {@link DynamicallyActivatableMetricsExporterService service} * * @return Whether the {@link DynamicallyActivatableMetricsExporterService service} was successfully unregistered. Returns false if a service with the given name was already registered and has been overwritten. */ diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java index 4cb5e78d55..5e18b09a71 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryImpl.java @@ -13,6 +13,7 @@ import lombok.extern.slf4j.Slf4j; import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils; +import javax.validation.constraints.NotNull; import java.util.Arrays; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -28,33 +29,25 @@ public class OpenTelemetryImpl implements OpenTelemetry { /** * The {@link OpenTelemetrySdk} implementation */ + @NotNull private OpenTelemetrySdk openTelemetry; @Builder.Default - private Object lock = new Object(); - - /** - * Gets the currently registered {@link OpenTelemetry} or {@link OpenTelemetry#noop()} if nothing has been registered. - * - * @return - */ - public OpenTelemetry get() { - return null == openTelemetry ? OpenTelemetry.noop() : openTelemetry; - } + private final Object lock = new Object(); @Override public TracerProvider getTracerProvider() { - return get().getTracerProvider(); + return openTelemetry.getTracerProvider(); } @Override public ContextPropagators getPropagators() { - return get().getPropagators(); + return openTelemetry.getPropagators(); } @Override public MeterProvider getMeterProvider() { - return get().getMeterProvider(); + return openTelemetry.getMeterProvider(); } /** @@ -75,15 +68,6 @@ private SdkTracerProvider getSdkTracerProvider() { return openTelemetry.getSdkTracerProvider(); } - /** - * Registers the {@link OpenTelemetrySdk}. If an {@link OpenTelemetrySdk} was already registered as {@link #openTelemetry}, flushes and closes the {@link SdkTracerProvider} and {@link SdkMeterProvider} of the previously registered {@link OpenTelemetrySdk} before registering the new {@link OpenTelemetrySdk}. - * - * @param openTelemetry The new {@link OpenTelemetrySdk} to register - */ - public void set(OpenTelemetrySdk openTelemetry) { - set(openTelemetry, true, true); - } - /** * Registers the {@link OpenTelemetry}. If an {@link OpenTelemetrySdk} was already registered as {@link #openTelemetry}, flush and close the {@link SdkTracerProvider} and {@link SdkMeterProvider} of the previously registered {@link OpenTelemetrySdk} if required before registering the new {@link OpenTelemetrySdk} * @@ -94,11 +78,9 @@ public void set(OpenTelemetrySdk openTelemetry) { public void set(OpenTelemetrySdk openTelemetry, boolean stopPreviousTracerProvider, boolean stopPreviousMeterProvider) { synchronized (lock) { if (null != openTelemetry) { - // stop previous SdkTracerProvider if settings changed if (stopPreviousTracerProvider) { OpenTelemetryUtils.stopTracerProvider(getSdkTracerProvider()); } - // stop previous SdkMeterProvider if settings changed if (stopPreviousMeterProvider) { OpenTelemetryUtils.stopMeterProvider(getSdkMeterProvider()); } @@ -131,24 +113,20 @@ public void flush() { CompletableResultCode resultCode = CompletableResultCode.ofAll(Arrays.asList(flushTracerProvider, flushMeterProvider)); if (!resultCode.isDone()) { CountDownLatch latch = new CountDownLatch(1); - resultCode.whenComplete(() -> latch.countDown()); + resultCode.whenComplete(latch::countDown); try { latch.await(15, TimeUnit.SECONDS); } catch (InterruptedException e) { - e.printStackTrace(); + log.error("Interrupted while waiting for the OpenTelemetryImpl to flush", e); } } } /** * Registers {@link OpenTelemetryImpl this} to {@link GlobalOpenTelemetry#set(OpenTelemetry)}. - * - * @return */ - public OpenTelemetryImpl registerGlobal() { - // register globally + public void registerGlobal() { GlobalOpenTelemetry.set(this); - return this; } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java index 740e3aac3b..b9ec8cde8f 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java @@ -15,7 +15,6 @@ import org.influxdb.dto.QueryResult; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.test.annotation.DirtiesContext; @@ -28,7 +27,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -@Disabled //TODO: Get this test to work on the CI +// @Disabled //TODO: Get this test to work on the CI public class InfluxExporterServiceIntTest extends SpringTestBase { private InfluxServer influx; diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java index 53cbffabd2..79174babd1 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java @@ -4,7 +4,6 @@ import io.github.netmikey.logunit.api.LogCapturer; import io.opencensus.trace.Tracing; import io.opencensus.trace.samplers.Samplers; -import org.assertj.core.api.AssertionsForClassTypes; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -89,6 +88,7 @@ void verifyTraceSent() throws InterruptedException { * Test for the {@link JaegerExporterService} using {@link ExporterServiceIntegrationTestBase} */ @Nested + @DirtiesContext class JaegerExporterServiceIntDockerTest extends ExporterServiceIntegrationTestBase { @Autowired @@ -103,7 +103,6 @@ void clearRequests() { * Test using the {@link TransportProtocol#GRPC} */ @Test - @DirtiesContext void verifyTraceSentGrpc() { updateProperties(mps -> { mps.setProperty("inspectit.exporters.tracing.jaeger.enabled", ExporterEnabledState.ENABLED); @@ -123,7 +122,6 @@ void verifyTraceSentGrpc() { * Test using the {@link TransportProtocol#HTTP_THRIFT} */ @Test - @DirtiesContext void verifyTraceSentThrift() { updateProperties(mps -> { mps.setProperty("inspectit.exporters.tracing.jaeger.enabled", ExporterEnabledState.ENABLED); @@ -153,6 +151,15 @@ class JaegerSettingsIntTest extends SpringTestBase { @Autowired InspectitEnvironment environment; + @BeforeEach + void beforeEach() { + System.out.printf("jaeger endpoint=" + environment.getCurrentConfig() + .getExporters() + .getTracing() + .getJaeger() + .getEndpoint()); + } + @DirtiesContext @Test void testEndpointNotSet() { @@ -174,42 +181,14 @@ void testProtocolNotSet() { } /** - * Test the default {@link rocks.inspectit.ocelot.config.model.exporters.trace.JaegerExporterSettings settings} of the {@link JaegerExporterService} + * Verifies the default {@link rocks.inspectit.ocelot.config.model.exporters.trace.JaegerExporterSettings settings} of the {@link JaegerExporterService} */ - @Test - void testDefault() { - // service should be disabled - assertThat(service.isEnabled()).isFalse(); - // enabled flag should be IF_CONFIGURED - assertThat(environment.getCurrentConfig() - .getExporters() - .getTracing() - .getJaeger() - .getEnabled()).isEqualTo(ExporterEnabledState.IF_CONFIGURED); - // endpoint should be null or empty - assertThat(environment.getCurrentConfig() - .getExporters() - .getTracing() - .getJaeger() - .getEndpoint()).isNullOrEmpty(); - assertThat(environment.getCurrentConfig() - .getExporters() - .getTracing() - .getJaeger() - .getProtocol()).isEqualTo(TransportProtocol.UNSET); - } - @Test void defaultSettings() { - // service is not running - AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); - JaegerExporterSettings jaeger = environment.getCurrentConfig().getExporters().getTracing().getJaeger(); - // enabled property is set to IF_CONFIGURED - assertThat(jaeger.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); - // endpoint is null or empty + assertThat(service.isEnabled()).isFalse(); + assertThat(jaeger.getEnabled()).isEqualTo(ExporterEnabledState.IF_CONFIGURED); assertThat(jaeger.getEndpoint()).isNullOrEmpty(); - // protocol is unset assertThat(jaeger.getProtocol()).isEqualTo(TransportProtocol.UNSET); } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java index 0bae0f204e..ba1a49c614 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/LoggingMetricsExporterServiceIntTest.java @@ -51,7 +51,7 @@ void enableService() { Awaitility.await() .atMost(15, TimeUnit.SECONDS) .pollInterval(1, TimeUnit.SECONDS) - .untilAsserted(() -> assertThat(Instances.openTelemetryController.isConfigured()).isTrue()); + .untilAsserted(() -> assertThat(Instances.openTelemetryController.isActive()).isTrue()); } @AfterEach diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java index 498031630b..28b2ab09f6 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java @@ -51,7 +51,7 @@ void clearRequests() { @Test void verifyMetricsWrittenGrpc() { updateProperties(mps -> { - mps.setProperty("inspectit.exporters.metrics.otlp.url", getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_METRICS_PATH)); + mps.setProperty("inspectit.exporters.metrics.otlp.endpoint", getEndpoint(COLLECTOR_OTLP_GRPC_PORT, OTLP_METRICS_PATH)); mps.setProperty("inspectit.exporters.metrics.otlp.export-interval", "500ms"); mps.setProperty("inspectit.exporters.metrics.otlp.enabled", ExporterEnabledState.ENABLED); mps.setProperty("inspectit.exporters.metrics.otlp.protocol", TransportProtocol.GRPC); @@ -70,7 +70,7 @@ void verifyMetricsWrittenGrpc() { @Test void verifyMetricsWrittenHttp() { updateProperties(mps -> { - mps.setProperty("inspectit.exporters.metrics.otlp.url", getEndpoint(COLLECTOR_OTLP_HTTP_PORT, OTLP_METRICS_PATH)); + mps.setProperty("inspectit.exporters.metrics.otlp.endpoint", getEndpoint(COLLECTOR_OTLP_HTTP_PORT, OTLP_METRICS_PATH)); mps.setProperty("inspectit.exporters.metrics.otlp.export-interval", "500ms"); mps.setProperty("inspectit.exporters.metrics.otlp.enabled", ExporterEnabledState.ENABLED); mps.setProperty("inspectit.exporters.metrics.otlp.protocol", TransportProtocol.HTTP_PROTOBUF); @@ -90,6 +90,7 @@ void verifyMetricsWrittenHttp() { void testNoEndpointSet() { updateProperties(props -> { props.setProperty("inspectit.exporters.metrics.otlp.endpoint", ""); + props.setProperty("inspectit.exporters.metrics.otlp.protocol", TransportProtocol.GRPC); props.setProperty("inspectit.exporters.metrics.otlp.enabled", ExporterEnabledState.ENABLED); }); warnLogs.assertContains("'endpoint'"); @@ -107,15 +108,10 @@ void testNoProtocolSet() { @Test void defaultSettings() { - // service is not running AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); - OtlpMetricsExporterSettings otlp = environment.getCurrentConfig().getExporters().getMetrics().getOtlp(); - // enabled property is set to IF_CONFIGURED assertThat(otlp.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); - // endpoint is null or empty assertThat(otlp.getEndpoint()).isNullOrEmpty(); - // protocol is UNSET assertThat(otlp.getProtocol()).isEqualTo(TransportProtocol.UNSET); } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java index 38b40cf066..98674768ee 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java @@ -94,15 +94,10 @@ void testNoProtocolSet() { @Test void defaultSettings() { - // service is not running AssertionsForClassTypes.assertThat(service.isEnabled()).isFalse(); - OtlpTraceExporterSettings otlp = environment.getCurrentConfig().getExporters().getTracing().getOtlp(); - // enabled property is set to IF_CONFIGURED assertThat(otlp.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); - // endpoint is null or empty assertThat(otlp.getEndpoint()).isNullOrEmpty(); - // protocol is unset assertThat(otlp.getProtocol()).isEqualTo(TransportProtocol.UNSET); } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/TransportProtocolSubsetValidatorTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/TransportProtocolSubsetValidatorTest.java new file mode 100644 index 0000000000..8b11c46d5b --- /dev/null +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/TransportProtocolSubsetValidatorTest.java @@ -0,0 +1,78 @@ +package rocks.inspectit.ocelot.config.validation; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocolSubset; +import rocks.inspectit.ocelot.config.model.exporters.TransportProtocolSubsetValidator; + +import javax.validation.ConstraintValidator; +import javax.validation.Validation; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; + +/** + * Test class for {@link rocks.inspectit.ocelot.config.model.exporters.TransportProtocolSubsetValidator} + */ +@ExtendWith(MockitoExtension.class) +public class TransportProtocolSubsetValidatorTest { + + private static ConstraintValidator validator; + + private static final Logger logger = LoggerFactory.getLogger(TransportProtocolSubsetValidatorTest.class); + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + HibernateConstraintValidatorContext ctx; + + @BeforeEach + void initMock() { + doReturn(ctx).when(ctx).unwrap(any()); + + } + + @BeforeAll + private static void beforeAll() { + // construct validator + validator = new TransportProtocolSubsetValidator(); + + Validation.buildDefaultValidatorFactory().getValidator(); + } + + @Test + void testSupportedTransportProtocol() { + DummyExporterSettings settings = new DummyExporterSettings(); + settings.setProtocol(TransportProtocol.GRPC); + assertThat(validator.isValid(new Object() { + + @TransportProtocolSubset(anyOf = {TransportProtocol.GRPC}) + private TransportProtocol transportProtocol = TransportProtocol.HTTP_PROTOBUF; + }, ctx)).isTrue(); + } + + private class DummyExporterSettings { + + @TransportProtocolSubset(anyOf = {TransportProtocol.GRPC}) + private TransportProtocol protocol; + + public void setProtocol(TransportProtocol protocol) { + this.protocol = protocol; + } + + public TransportProtocol getProtocol() { + return protocol; + } + } + +} + + diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java index 781ae5329f..e5f11c15a0 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/ZipkinExporterServiceIntTest.java @@ -6,20 +6,26 @@ import io.opencensus.trace.samplers.Samplers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.PropertySource; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState; +import rocks.inspectit.ocelot.config.model.exporters.trace.ZipkinExporterSettings; import rocks.inspectit.ocelot.core.SpringTestBase; +import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import java.util.concurrent.TimeUnit; import static com.github.tomakehurst.wiremock.client.WireMock.*; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @TestPropertySource(properties = {"inspectit.exporters.tracing.zipkin.endpoint=http://127.0.0.1:9411/api/v2/spans", "inspectit.tracing.max-export-batch-size=512", "inspectit.tracing.schedule-delay-millis=20000"}) @@ -75,4 +81,39 @@ void testNoEndpointSet() { }); warnLogs.assertContains("'endpoint'"); } + + @Nested + class ZipkinExporterSettingsIntTest { + + /** + * Remove all {@link PropertySource} that are not part of the original inspectIT environment + */ + @BeforeEach + void resetPropertySources() { + environment.updatePropertySources(propertySources -> { + for (PropertySource prop : environment.getPropertySources()) { + if (!prop.getName().toLowerCase().contains("inspectit")) { + propertySources.remove(prop.getName()); + } + } + }); + } + + @Autowired + InspectitEnvironment environment; + + @Autowired + ZipkinExporterService service; + + /** + * Verifies the default {@link rocks.inspectit.ocelot.config.model.exporters.trace.ZipkinExporterSettings settings} + */ + @Test + void defaultSettings() { + ZipkinExporterSettings zipkin = environment.getCurrentConfig().getExporters().getTracing().getZipkin(); + assertThat(service.isEnabled()).isFalse(); + assertThat(zipkin.getEnabled()).isEqualTo(ExporterEnabledState.IF_CONFIGURED); + assertThat(zipkin.getEndpoint()).isNullOrEmpty(); + } + } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java index f77d502988..13c491743e 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java @@ -6,7 +6,6 @@ import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.exporter.logging.LoggingMetricExporter; import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.trace.SdkTracerProvider; @@ -25,7 +24,7 @@ import rocks.inspectit.ocelot.config.model.InspectitConfig; import rocks.inspectit.ocelot.core.config.InspectitEnvironment; import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService; -import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableTraceExporterService; +import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService; import java.lang.reflect.Method; @@ -59,7 +58,7 @@ void initOpenTelemetryController() { @Test void testShutdown() { openTelemetryController.shutdown(); - assertThat(openTelemetryController.isStopped()); + assertThat(openTelemetryController.isShutdown()); } @Nested @@ -73,7 +72,7 @@ class ConfigureTracing { @BeforeEach protected void init() { - openTelemetryController.setSpanExporter(spanExporter); + openTelemetryController.setMultiSpanExporter(spanExporter); clearInvocations(openTelemetryController); } @@ -84,9 +83,9 @@ protected void init() { */ protected void registerTestTraceExporterServiceAndVerify(boolean expected) { int registeredServices = openTelemetryController.registeredTraceExportServices.size(); - assertThat(openTelemetryController.registerTraceExporterService(testTraceExporterService)).isEqualTo(expected); + assertThat(openTelemetryController.registerTraceExporterService(testTraceExporterService.getSpanExporter(), testTraceExporterService.getName())).isEqualTo(expected); assertThat(openTelemetryController.registeredTraceExportServices.size()).isEqualTo(registeredServices + (expected ? 1 : 0)); - verify(openTelemetryController, times(1)).registerTraceExporterService(testTraceExporterService); + verify(openTelemetryController, times(1)).registerTraceExporterService(testTraceExporterService.getSpanExporter(), testTraceExporterService.getName()); verify(openTelemetryController, times(expected ? 1 : 0)).notifyTracingSettingsChanged(); verify(spanExporter, times(1)).registerSpanExporter(testTraceExporterService.getName(), testTraceExporterService.getSpanExporter()); verifyNoMoreInteractions(openTelemetryController, spanExporter); @@ -100,9 +99,9 @@ protected void registerTestTraceExporterServiceAndVerify(boolean expected) { */ protected void unregisterTestTraceExporterServiceAndVerify(boolean expected) { int registeredServices = openTelemetryController.registeredTraceExportServices.size(); - assertThat(openTelemetryController.unregisterTraceExporterService(testTraceExporterService)).isEqualTo(expected); + assertThat(openTelemetryController.unregisterTraceExporterService(testTraceExporterService.getName())).isEqualTo(expected); assertThat(openTelemetryController.registeredTraceExportServices.size()).isEqualTo(registeredServices - (expected ? 1 : 0)); - verify(openTelemetryController, times(1)).unregisterTraceExporterService(testTraceExporterService); + verify(openTelemetryController, times(1)).unregisterTraceExporterService(testTraceExporterService.getName()); verify(openTelemetryController, times(expected ? 1 : 0)).notifyTracingSettingsChanged(); verify(spanExporter, times(1)).unregisterSpanExporter(testTraceExporterService.getName()); verifyNoMoreInteractions(openTelemetryController, spanExporter); @@ -143,7 +142,7 @@ void testConfigureTracing() { assertThat(openTelemetryController.configureOpenTelemetry()).isTrue(); // tracing should have changed but metrics not verify(openTelemetryController, times(1)).configureTracing(any(InspectitConfig.class)); - verify(openTelemetryController, times(0)).configureMeterProvider(any(InspectitConfig.class)); + verify(openTelemetryController, times(0)).configureMeterProvider(); // verify that the tracer provider does not change after tracing has been (re-)configured assertThat(globalTracerProvider).isSameAs(GlobalOpenTelemetry.getTracerProvider()); @@ -157,11 +156,10 @@ void testShutdown() { } /** - * A noop {@link DynamicallyActivatableTraceExporterService} for testing + * A noop {@link DynamicallyActivatableService trace exporter service} for testing */ - class TestTraceExporterService extends DynamicallyActivatableTraceExporterService { + class TestTraceExporterService extends DynamicallyActivatableService { - @Override public SpanExporter getSpanExporter() { try { Method m = Class.forName("io.opentelemetry.sdk.trace.export.NoopSpanExporter") @@ -265,7 +263,7 @@ void testConfigureMetrics() { assertThat(openTelemetryController.configureOpenTelemetry()).isTrue(); // verify that meter provider was configured but not tracing - verify(openTelemetryController, times(1)).configureMeterProvider(any(InspectitConfig.class)); + verify(openTelemetryController, times(1)).configureMeterProvider(); verify(openTelemetryController, times(0)).configureTracing(any(InspectitConfig.class)); // the meter provider should have changed, but the tracer provider not @@ -283,16 +281,13 @@ void testShutdown() { } /** - * A noop {@link DynamicallyActivatableMetricsExporterService} for testing + * A noop {@link DynamicallyActivatableService metric exporter service} for testing */ class TestMetricsExporterService extends DynamicallyActivatableMetricsExporterService { - @Mock - MetricExporter metricExporter; - @Override public MetricReaderFactory getNewMetricReaderFactory() { - return PeriodicMetricReader.newMetricReaderFactory(new LoggingMetricExporter()); + return PeriodicMetricReader.newMetricReaderFactory(LoggingMetricExporter.create()); } @Override diff --git a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md index 7ab3f08aeb..a888f73670 100644 --- a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md +++ b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md @@ -6,7 +6,7 @@ title: Breaking Changes ### Integration of the OpenTelemetry OpenCensus Shim -Starting with the current release, inspectIT Ocelot migrates from OpenCensus to [OpenTelemetry](https://github.com/open-telemetry). As a first step, we include the [OpenTelemetry OpenCensus Shim](https://github.com/open-telemetry/opentelemetry-java/tree/main/opencensus-shim). InspectIT Ocelot still uses and supports the [OpenCensus-API](https://opencensus.io/quickstart/java/), but the exporter implementations of OpenTelemetry are used. +Starting with the current release, inspectIT Ocelot migrates from OpenCensus to [OpenTelemetry](https://github.com/open-telemetry). As a first step, we include the [OpenTelemetry OpenCensus Shim](https://github.com/open-telemetry/opentelemetry-java/tree/main/opencensus-shim). inspectIT Ocelot still uses and supports the [OpenCensus-API](https://opencensus.io/quickstart/java/), but the exporter implementations of OpenTelemetry are used. ### Updated and removed exporter @@ -16,7 +16,7 @@ Due to the migration from OpenCensus to OpenTelemetry, the `OpenCensus Agent Exp #### Added `OTLPMetricsExporter` and `OTLPTraceExporter` -Due to the migration to OpenTelemetry, InspectIT Ocelot now supports OpenTelemetry Protocol (OTLP) exporter for metrics and tracing. +Due to the migration to OpenTelemetry, inspectIT Ocelot now supports OpenTelemetry Protocol (OTLP) exporter for metrics and tracing. #### Exporter property `url` and `grpc` replaced by `endpoint` diff --git a/inspectit-ocelot-documentation/docs/tracing/tracing.md b/inspectit-ocelot-documentation/docs/tracing/tracing.md index bcf57a16ff..5f4e156760 100644 --- a/inspectit-ocelot-documentation/docs/tracing/tracing.md +++ b/inspectit-ocelot-documentation/docs/tracing/tracing.md @@ -29,8 +29,10 @@ You can additionally define the following global properties (`inspectit.tracing- |---|---|---| |`max-export-batch-size`|512|The max export batch size for every export, i.e., the maximum number of spans exported by the used `BatchSpanProcessor`| |`schedule-delay-millis`|5000|The delay interval between two consecutive exports in milliseconds. -**Note**: These properties take only effect once when the agent is starting. If you change these properties while the agent is running, they will not take effect until the agent retarted. +:::warning +These properties take only effect once when the agent is starting. If you change these properties while the agent is running, they will not take effect until the agent retarted. +::: ### Common Tags as Attributes Globally defined [common tags](metrics/common-tags.md) used when recording metrics can also be inserted as attributes in traces. From 204042a72d4fbaeb64ca07e5b67db2784c86e6a7 Mon Sep 17 00:00:00 2001 From: Heiko Holz Date: Thu, 5 May 2022 17:39:20 +0200 Subject: [PATCH 75/86] fix(validation): refactor transport protocol validation --- .../exporters/TransportProtocolSubset.java | 6 +-- .../TransportProtocolSubsetValidator.java | 8 +-- .../TransportProtocolSubsetValidatorTest.java | 54 ++++++++----------- 3 files changed, 28 insertions(+), 40 deletions(-) diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubset.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubset.java index bc9e8a7df1..26ffe72a8f 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubset.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubset.java @@ -1,6 +1,4 @@ -package rocks.inspectit.ocelot.config.utils; - -import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; +package rocks.inspectit.ocelot.config.model.exporters; import javax.validation.Constraint; import javax.validation.Payload; @@ -23,7 +21,7 @@ TransportProtocol[] anyOf(); - String message() default "must be any of {anyOf}"; + String message() default "Wrong 'protocol' is specified. Supported values are {anyOf}."; Class[] groups() default {}; diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubsetValidator.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubsetValidator.java index b21ae3751b..0f7f484efe 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubsetValidator.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/exporters/TransportProtocolSubsetValidator.java @@ -1,11 +1,13 @@ -package rocks.inspectit.ocelot.config.utils; - -import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; +package rocks.inspectit.ocelot.config.model.exporters; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.Arrays; +/** + * Validator for {@link TransportProtocolSubset} + */ + public class TransportProtocolSubsetValidator implements ConstraintValidator { private TransportProtocol[] subset; diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/TransportProtocolSubsetValidatorTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/TransportProtocolSubsetValidatorTest.java index 8b11c46d5b..0f971fbaea 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/TransportProtocolSubsetValidatorTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/TransportProtocolSubsetValidatorTest.java @@ -1,25 +1,20 @@ -package rocks.inspectit.ocelot.config.validation; +package rocks.inspectit.ocelot.core.exporter; -import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Answers; -import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import rocks.inspectit.ocelot.config.model.exporters.TransportProtocol; import rocks.inspectit.ocelot.config.model.exporters.TransportProtocolSubset; -import rocks.inspectit.ocelot.config.model.exporters.TransportProtocolSubsetValidator; -import javax.validation.ConstraintValidator; +import javax.validation.ConstraintViolation; import javax.validation.Validation; +import javax.validation.Validator; +import java.util.Set; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; /** * Test class for {@link rocks.inspectit.ocelot.config.model.exporters.TransportProtocolSubsetValidator} @@ -27,50 +22,43 @@ @ExtendWith(MockitoExtension.class) public class TransportProtocolSubsetValidatorTest { - private static ConstraintValidator validator; - private static final Logger logger = LoggerFactory.getLogger(TransportProtocolSubsetValidatorTest.class); - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - HibernateConstraintValidatorContext ctx; + private Validator validator; @BeforeEach - void initMock() { - doReturn(ctx).when(ctx).unwrap(any()); - - } - - @BeforeAll - private static void beforeAll() { - // construct validator - validator = new TransportProtocolSubsetValidator(); - - Validation.buildDefaultValidatorFactory().getValidator(); + private void beforeEach() { + validator = Validation.buildDefaultValidatorFactory().getValidator(); } @Test void testSupportedTransportProtocol() { DummyExporterSettings settings = new DummyExporterSettings(); settings.setProtocol(TransportProtocol.GRPC); - assertThat(validator.isValid(new Object() { + Set> violations = validator.validate(settings); + assertThat(violations.isEmpty()).isTrue(); + } - @TransportProtocolSubset(anyOf = {TransportProtocol.GRPC}) - private TransportProtocol transportProtocol = TransportProtocol.HTTP_PROTOBUF; - }, ctx)).isTrue(); + @Test + void testUnsupportedTransportProtocol() { + DummyExporterSettings settings = new DummyExporterSettings(); + settings.setProtocol(TransportProtocol.HTTP_PROTOBUF); + Set> violations = validator.validate(settings); + assertThat(violations.isEmpty()).isFalse(); + assertThat(violations.stream().anyMatch(vio -> vio.getMessage().contains("'protocol'"))).isTrue(); + for (ConstraintViolation violation : violations) { + System.out.println(violation.getMessage()); + } } private class DummyExporterSettings { - @TransportProtocolSubset(anyOf = {TransportProtocol.GRPC}) + @TransportProtocolSubset(anyOf = {TransportProtocol.GRPC, TransportProtocol.HTTP_THRIFT}) private TransportProtocol protocol; public void setProtocol(TransportProtocol protocol) { this.protocol = protocol; } - - public TransportProtocol getProtocol() { - return protocol; - } } } From 6611bd8c0e620f4fe0917e29b9d9391f8ecdc94d Mon Sep 17 00:00:00 2001 From: Marius Oehler Date: Wed, 11 May 2022 23:56:53 +0200 Subject: [PATCH 76/86] Small refactorings --- .../TraceExportersConfiguration.java | 2 +- gradle.properties | 2 +- .../StringToTransportProtocolConverter.java | 2 +- .../metrics/InfluxExporterSettings.java | 9 ++++----- .../core/exporter/JaegerExporterService.java | 3 +-- .../OpenTelemetryControllerImpl.java | 2 +- .../core/opentelemetry/OpenTelemetryImpl.java | 12 +++++------- .../exporter/InfluxExporterServiceIntTest.java | 16 ++++++++++++++-- .../exporter/JaegerExporterServiceIntTest.java | 2 +- .../OtlpMetricsExporterServiceIntTest.java | 2 +- .../OtlpTraceExporterServiceIntTest.java | 2 +- 11 files changed, 31 insertions(+), 23 deletions(-) diff --git a/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java b/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java index 0329d90a76..d34da294c8 100644 --- a/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java +++ b/components/inspectit-ocelot-eum-server/src/main/java/rocks/inspectit/oce/eum/server/exporters/configuration/TraceExportersConfiguration.java @@ -44,7 +44,7 @@ public void logWrongJaegerConfig() { @ConditionalOnProperty({"inspectit-eum-server.exporters.tracing.jaeger.enabled", "inspectit-eum-server.exporters.tracing.jaeger.endpoint"}) @ConditionalOnExpression("(NOT new String('${inspectit-eum-server.exporters.tracing.jaeger.enabled}').toUpperCase().equals(T(rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState).DISABLED.toString())) AND (new String('${inspectit-eum-server.exporters.tracing.jaeger.endpoint}').length() > 0)") public SpanExporter jaegerSpanExporter() { - @Valid JaegerExporterSettings jaegerExporterSettings = configuration.getExporters().getTracing().getJaeger(); + JaegerExporterSettings jaegerExporterSettings = configuration.getExporters().getTracing().getJaeger(); ManagedChannel channel = ManagedChannelBuilder.forTarget(jaegerExporterSettings.getEndpoint()) .usePlaintext() diff --git a/gradle.properties b/gradle.properties index ee0f6ec4ba..184c266eb6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ openCensusVersion=0.28.3 openTelemetryVersion=1.10.0 openTelemetryAlphaVersion=1.10.0-alpha -prometheusClientVersion=0.6.0 +prometheusClientVersion=0.15.0 # appropriate netty version, see https://github.com/census-instrumentation/opencensus-java/blob/master/exporters/trace/ocagent/README.md tcnativeVersion=2.0.20.Final grpcVersion=1.43.1 diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/conversion/StringToTransportProtocolConverter.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/conversion/StringToTransportProtocolConverter.java index f4aa6dffa7..07fcbd99ff 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/conversion/StringToTransportProtocolConverter.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/conversion/StringToTransportProtocolConverter.java @@ -12,6 +12,6 @@ public class StringToTransportProtocolConverter implements Converter { + try { + InfluxDB influxDB = InfluxDBFactory.connect(url, user, password); + Pong ping = influxDB.ping(); + assertThat(ping.isGood()).isTrue(); + } catch (InfluxDBIOException exception) { + // ignore + } + }); } @AfterEach @@ -74,7 +85,8 @@ void verifyInfluxDataWritten() { TagKey testTag = TagKey.create("my_tag"); Measure.MeasureDouble testMeasure = Measure.MeasureDouble.create("my/test/measure", "foo", "bars"); - View testView = View.create(View.Name.create("my/test/measure/cool%data"), "", testMeasure, Aggregation.Sum.create(), Arrays.asList(testTag)); + View testView = View.create(View.Name.create("my/test/measure/cool%data"), "", testMeasure, Aggregation.Sum.create(), Arrays + .asList(testTag)); Stats.getViewManager().registerView(testView); try (Scope tc = Tags.getTagger().emptyBuilder().putLocal(testTag, TagValue.create("myval")).buildScoped()) { diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java index 79174babd1..23334b83be 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java @@ -189,7 +189,7 @@ void defaultSettings() { assertThat(service.isEnabled()).isFalse(); assertThat(jaeger.getEnabled()).isEqualTo(ExporterEnabledState.IF_CONFIGURED); assertThat(jaeger.getEndpoint()).isNullOrEmpty(); - assertThat(jaeger.getProtocol()).isEqualTo(TransportProtocol.UNSET); + assertThat(jaeger.getProtocol()).isNull(); } /** diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java index 28b2ab09f6..f0508dd42d 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpMetricsExporterServiceIntTest.java @@ -112,6 +112,6 @@ void defaultSettings() { OtlpMetricsExporterSettings otlp = environment.getCurrentConfig().getExporters().getMetrics().getOtlp(); assertThat(otlp.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); assertThat(otlp.getEndpoint()).isNullOrEmpty(); - assertThat(otlp.getProtocol()).isEqualTo(TransportProtocol.UNSET); + assertThat(otlp.getProtocol()).isNull(); } } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java index 98674768ee..480abf32d1 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/OtlpTraceExporterServiceIntTest.java @@ -98,7 +98,7 @@ void defaultSettings() { OtlpTraceExporterSettings otlp = environment.getCurrentConfig().getExporters().getTracing().getOtlp(); assertThat(otlp.getEnabled().equals(ExporterEnabledState.IF_CONFIGURED)); assertThat(otlp.getEndpoint()).isNullOrEmpty(); - assertThat(otlp.getProtocol()).isEqualTo(TransportProtocol.UNSET); + assertThat(otlp.getProtocol()).isNull(); } } From f538ce456066900ca97de731a269b7da299bf692 Mon Sep 17 00:00:00 2001 From: Marius Oehler Date: Thu, 12 May 2022 00:00:01 +0200 Subject: [PATCH 77/86] temporarily adding otel migration branch to ci build --- .github/workflows/agent_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/agent_test.yml b/.github/workflows/agent_test.yml index 22cabe5ff2..46d874d342 100644 --- a/.github/workflows/agent_test.yml +++ b/.github/workflows/agent_test.yml @@ -7,6 +7,7 @@ on: pull_request: branches: - master + - feature/opentelemetry-migration paths-ignore: - 'components/**' - 'inspectit-ocelot-documentation/**' From 0eac28ec3b61145039c1febd76e7ce437a752b67 Mon Sep 17 00:00:00 2001 From: Marius Brill Date: Thu, 12 May 2022 10:20:09 +0200 Subject: [PATCH 78/86] Set Wiremock to use localhost --- .../ocelot/core/exporter/JaegerExporterServiceIntTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java index 23334b83be..c5a2a26b20 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java @@ -49,7 +49,7 @@ public class JaegerExporterServiceIntTest { */ @DirtiesContext @Nested - @TestPropertySource(properties = {"inspectit.exporters.tracing.jaeger.endpoint=http://127.0.0.1:14268/api/traces", "inspectit.exporters.tracing.jaeger.protocol=http/thrift", "inspectit.tracing.max-export-batch-size=1"}) + @TestPropertySource(properties = {"inspectit.exporters.tracing.jaeger.endpoint=http://localhost:14268/api/traces", "inspectit.exporters.tracing.jaeger.protocol=http/thrift", "inspectit.tracing.max-export-batch-size=1"}) class JaegerThriftExporterServiceIntTest extends SpringTestBase { private WireMockServer wireMockServer; From 69cd5684fc839e2ab19556fb4952dce2b1f141a9 Mon Sep 17 00:00:00 2001 From: Marius Oehler Date: Fri, 13 May 2022 08:06:20 +0200 Subject: [PATCH 79/86] refactored final modifier removal in jdk12+ --- .../core/utils/OpenCensusShimUtils.java | 5 +- .../ocelot/core/utils/ReflectionUtils.java | 48 ++++++++++--------- .../InfluxExporterServiceIntTest.java | 35 ++++++-------- .../JaegerExporterServiceIntTest.java | 7 +-- 4 files changed, 43 insertions(+), 52 deletions(-) diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java index 6de4b5780b..6805163e82 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtils.java @@ -55,9 +55,8 @@ public static Tracer getOpenTelemetryTracerOfOpenTelemetrySpanBuilderImpl() { try { Field tracerField = null; // make the field accessible - tracerField = ReflectionUtils.getFinalStaticFieldAndMakeAccessible(Class.forName("io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl"), "OTEL_TRACER", true); - Tracer tracer = (Tracer) tracerField.get(null); - return tracer; + tracerField = ReflectionUtils.getAccessibleField(Class.forName("io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl"), "OTEL_TRACER"); + return (Tracer) tracerField.get(null); } catch (Exception e) { log.error("Failed to get OTEL_TRACER of OpenTelemetrySpanBuilderImpl"); return null; diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java index 3094e0e477..329d69a460 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/utils/ReflectionUtils.java @@ -1,6 +1,10 @@ package rocks.inspectit.ocelot.core.utils; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.JavaVersion; +import org.apache.commons.lang3.SystemUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import sun.misc.Unsafe; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -20,9 +24,7 @@ public class ReflectionUtils { */ public static Field makeFieldAccessibleAndRemoveFinal(Field field) throws NoSuchFieldException, IllegalAccessException { field.setAccessible(true); - Field modifiers = Field.class.getDeclaredField("modifiers"); - modifiers.setAccessible(true); - modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); + FieldUtils.removeFinalModifier(field); return field; } @@ -36,33 +38,33 @@ public static Field makeFieldAccessibleAndRemoveFinal(Field field) throws NoSuch * @throws IllegalAccessException */ public static void setFinalStatic(Field field, Object newValue) throws NoSuchFieldException, IllegalAccessException { - field = makeFieldAccessibleAndRemoveFinal(field); - field.set(null, newValue); + // see https://stackoverflow.com/questions/61141836/change-static-final-field-in-java-12 + if (SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_12)) { + Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + Unsafe unsafe = (Unsafe) unsafeField.get(null); + + Object staticFieldBase = unsafe.staticFieldBase(field); + long staticFieldOffset = unsafe.staticFieldOffset(field); + unsafe.putObject(staticFieldBase, staticFieldOffset, newValue); + } else { + field = makeFieldAccessibleAndRemoveFinal(field); + field.set(null, newValue); + } } /** - * Gets the final static field for the given {@link Class}, makes it accessible + * Gets the field for the given {@link Class}, makes it accessible * - * @param clazz The {@link Class} that contains the final static field - * @param fieldName The name of the field - * @param removeFinal Whether to remove the {@link Modifier#FINAL} modifier from the field + * @param clazz The {@link Class} that contains the final static field + * @param fieldName The name of the field * - * @return - * - * @throws NoSuchFieldException - * @throws IllegalAccessException + * @return the specified field of the given class */ - public static Field getFinalStaticFieldAndMakeAccessible(Class clazz, String fieldName, boolean removeFinal) throws NoSuchFieldException, IllegalAccessException { - Field field = clazz.getDeclaredField(fieldName); - - // make field accessible - if (removeFinal) { - ReflectionUtils.makeFieldAccessibleAndRemoveFinal(field); - } else { - field.setAccessible(true); - } - + public static Field getAccessibleField(Class clazz, String fieldName) throws NoSuchFieldException, IllegalAccessException { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); return field; } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java index f3a8783d9c..9474bf5ad6 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/InfluxExporterServiceIntTest.java @@ -50,16 +50,6 @@ void startInfluxDB() throws Exception { influx = builder.build(); influx.start(); url = "http://localhost:" + freeHttpPort; - - await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { - try { - InfluxDB influxDB = InfluxDBFactory.connect(url, user, password); - Pong ping = influxDB.ping(); - assertThat(ping.isGood()).isTrue(); - } catch (InfluxDBIOException exception) { - // ignore - } - }); } @AfterEach @@ -100,17 +90,20 @@ void verifyInfluxDataWritten() { } await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { - InfluxDB iDB = InfluxDBFactory.connect(url, user, password); // note: user and password are mandatory as of v1.15.0 - QueryResult result = iDB.query(new Query("SELECT LAST(cool_data) FROM " + DATABASE + ".autogen.my_test_measure GROUP BY *")); - - List results = result.getResults(); - assertThat(results).hasSize(1); - QueryResult.Result data = results.get(0); - assertThat(data.getSeries()).hasSize(1); - QueryResult.Series series = data.getSeries().get(0); - assertThat(series.getTags()).hasSize(1).containsEntry("my_tag", "myval"); - assertThat(series.getValues().get(0).get(1)).isEqualTo(42.0); - + try { + InfluxDB iDB = InfluxDBFactory.connect(url, user, password); // note: user and password are mandatory as of v1.15.0 + QueryResult result = iDB.query(new Query("SELECT LAST(cool_data) FROM " + DATABASE + ".autogen.my_test_measure GROUP BY *")); + + List results = result.getResults(); + assertThat(results).hasSize(1); + QueryResult.Result data = results.get(0); + assertThat(data.getSeries()).hasSize(1); + QueryResult.Series series = data.getSeries().get(0); + assertThat(series.getTags()).hasSize(1).containsEntry("my_tag", "myval"); + assertThat(series.getValues().get(0).get(1)).isEqualTo(42.0); + } catch (InfluxDBIOException exception) { + // ignore + } }); } diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java index c5a2a26b20..2d7f3efd37 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/exporter/JaegerExporterServiceIntTest.java @@ -71,14 +71,11 @@ void cleanup() { @Test void verifyTraceSent() throws InterruptedException { Tracing.getTracer().spanBuilder("jaegerspan").setSampler(Samplers.alwaysSample()).startSpanAndRun(() -> { + System.out.println("dummy runnable"); }); - logger.info("Wait for Jaeger to process the span..."); - Thread.sleep(1100L); - - Instances.openTelemetryController.flush(); - await().atMost(15, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { + Instances.openTelemetryController.flush(); verify(postRequestedFor(urlPathEqualTo(JAEGER_THRIFT_PATH))); }); } From 92e07731c448d89d090283b2470601f9ba16cc11 Mon Sep 17 00:00:00 2001 From: Marius Oehler Date: Fri, 13 May 2022 12:18:32 +0200 Subject: [PATCH 80/86] fix tests for jdk +12 --- .../inspectit/ocelot/utils/TestUtils.java | 25 ++++++++++++------- .../HighPrecisionTimerTest.java | 0 .../OpenCensusShimUtilsTest.java | 3 +-- 3 files changed, 17 insertions(+), 11 deletions(-) rename inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/{util => utils}/HighPrecisionTimerTest.java (100%) rename inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/{util => utils}/OpenCensusShimUtilsTest.java (94%) diff --git a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java index a254d235fb..876cc4b9b8 100644 --- a/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java +++ b/inspectit-ocelot-agent/src/system-test/java/rocks/inspectit/ocelot/utils/TestUtils.java @@ -27,6 +27,7 @@ import rocks.inspectit.ocelot.bootstrap.AgentManager; import rocks.inspectit.ocelot.bootstrap.Instances; import rocks.inspectit.ocelot.bootstrap.opentelemetry.NoopOpenTelemetryController; +import sun.misc.Unsafe; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -109,7 +110,8 @@ private static synchronized Cache, Object> getInstrumentationCache() { getBean.setAccessible(true); Object instrumentationManager = getBean.invoke(ctx, "instrumentationManager"); - activeInstrumentations = (Cache, Object>) getField(instrumentationManager.getClass(), "activeInstrumentations").get(instrumentationManager); + activeInstrumentations = (Cache, Object>) getField(instrumentationManager.getClass(), "activeInstrumentations") + .get(instrumentationManager); } catch (Exception ex) { throw new RuntimeException(ex); } @@ -122,14 +124,14 @@ private static synchronized Cache, Object> getInstrumentationCache() { * This does not wait for potential hooks which will be created. */ public static void waitForClassInstrumentations(Class... clazz) { - waitForClassInstrumentations(Arrays.asList(clazz), false, 15, TimeUnit.MINUTES); + waitForClassInstrumentations(Arrays.asList(clazz), false, 15, TimeUnit.SECONDS); } /** * Waits until a hook for each of the given classes exist. */ public static void waitForClassHooks(Class... clazz) { - waitForClassHooks(Arrays.asList(clazz), 10, TimeUnit.MINUTES); + waitForClassHooks(Arrays.asList(clazz), 10, TimeUnit.SECONDS); } /** @@ -339,12 +341,7 @@ public static InMemorySpanExporter initializeOpenTelemetryForSystemTesting() { Tracer tracer = GlobalOpenTelemetry.getTracer("io.opentelemetry.opencensusshim"); Field tracerField = Class.forName("io.opentelemetry.opencensusshim.OpenTelemetrySpanBuilderImpl") .getDeclaredField("OTEL_TRACER"); - // set static final field - tracerField.setAccessible(true); - Field modifiers = Field.class.getDeclaredField("modifiers"); - modifiers.setAccessible(true); - modifiers.setInt(tracerField, tracerField.getModifiers() & ~Modifier.FINAL); - tracerField.set(null, tracer); + setFinalField(tracerField, tracer); logger.info("OTEL_TRACER updated to {} ({})", tracer, openTelemetry.getTracer("io.opentelemetry.opencensusshim")); } catch (Exception e) { @@ -354,6 +351,16 @@ public static InMemorySpanExporter initializeOpenTelemetryForSystemTesting() { return inMemSpanExporter; } + private static void setFinalField(Field field, Object newValue) throws Exception { + Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + Unsafe unsafe = (Unsafe) unsafeField.get(null); + + Object staticFieldBase = unsafe.staticFieldBase(field); + long staticFieldOffset = unsafe.staticFieldOffset(field); + unsafe.putObject(staticFieldBase, staticFieldOffset, newValue); + } + private static long getInstrumentationQueueLength() { ViewManager viewManager = Stats.getViewManager(); AggregationData.LastValueDataLong queueSize = (AggregationData.LastValueDataLong) viewManager.getView(View.Name.create("inspectit/self/instrumentation-queue-size")) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/HighPrecisionTimerTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java similarity index 100% rename from inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/HighPrecisionTimerTest.java rename to inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/OpenCensusShimUtilsTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtilsTest.java similarity index 94% rename from inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/OpenCensusShimUtilsTest.java rename to inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtilsTest.java index cf725beb8e..775ed90ecc 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/util/OpenCensusShimUtilsTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/OpenCensusShimUtilsTest.java @@ -1,11 +1,10 @@ -package rocks.inspectit.ocelot.core.util; +package rocks.inspectit.ocelot.core.utils; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.sdk.OpenTelemetrySdk; import org.junit.jupiter.api.Test; import rocks.inspectit.ocelot.core.SpringTestBase; -import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils; import static org.assertj.core.api.Assertions.assertThat; From 4db1e60e05cdaac4ea80fa5aad026b32905361a7 Mon Sep 17 00:00:00 2001 From: Marius Brill Date: Fri, 13 May 2022 13:13:11 +0200 Subject: [PATCH 81/86] Update HighPrecisionTimerTest to use await().atLeast() --- .../core/utils/HighPrecisionTimerTest.java | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java index 7150c5f7cd..967587218e 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java @@ -6,8 +6,10 @@ import org.mockito.Mockito; import java.time.Duration; +import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; +import static org.awaitility.Awaitility.await; import static org.mockito.Mockito.*; public class HighPrecisionTimerTest { @@ -23,18 +25,6 @@ void cleanUp() { timer.destroy(); } - private void sleepAtLeast(long ms) { - long start = System.nanoTime(); - while ((System.nanoTime() - start) < ms * 1000 * 1000) { - long slept = (System.nanoTime() - start) / 1000 / 1000; - try { - Thread.sleep(ms - slept); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - } - @Nested class Start { @@ -43,7 +33,7 @@ void verifyStartResetsInactivity() { createSpyTimer(1, 100, () -> false); for (int i = 0; i < 10; i++) { timer.start(); - sleepAtLeast(20); + await().atLeast(5, TimeUnit.SECONDS).until(timer::isStarted); } verify(timer).startTimerSynchronized(); } @@ -57,7 +47,7 @@ void verifyActionCalled() { createSpyTimer(1, 10, action); timer.start(); - sleepAtLeast(50); + await().atLeast(5, TimeUnit.SECONDS).until(timer::isStarted); verify(action, atLeastOnce()).getAsBoolean(); } @@ -67,7 +57,7 @@ void verifyActionResetsInactivity() { createSpyTimer(1, 10, () -> true); timer.start(); - sleepAtLeast(50); + await().atLeast(5, TimeUnit.SECONDS).until(timer::isStarted); timer.start(); verify(timer).startTimerSynchronized(); @@ -81,7 +71,7 @@ void verifyRestartAfterInactivity() { } verify(timer).startTimerSynchronized(); - sleepAtLeast(150); + await().atLeast(5, TimeUnit.SECONDS).until(timer::isStarted); for (int i = 0; i < 2; i++) { timer.start(); } From c6e8044ae8b2179e896c308bf709808463968179 Mon Sep 17 00:00:00 2001 From: Marius Brill Date: Fri, 13 May 2022 13:13:11 +0200 Subject: [PATCH 82/86] Update HighPrecisionTimerTest to use await().atLeast() --- .../core/utils/HighPrecisionTimerTest.java | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java index 7150c5f7cd..3c9a280fe7 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java @@ -6,8 +6,10 @@ import org.mockito.Mockito; import java.time.Duration; +import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; +import static org.awaitility.Awaitility.await; import static org.mockito.Mockito.*; public class HighPrecisionTimerTest { @@ -23,18 +25,6 @@ void cleanUp() { timer.destroy(); } - private void sleepAtLeast(long ms) { - long start = System.nanoTime(); - while ((System.nanoTime() - start) < ms * 1000 * 1000) { - long slept = (System.nanoTime() - start) / 1000 / 1000; - try { - Thread.sleep(ms - slept); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - } - @Nested class Start { @@ -43,7 +33,7 @@ void verifyStartResetsInactivity() { createSpyTimer(1, 100, () -> false); for (int i = 0; i < 10; i++) { timer.start(); - sleepAtLeast(20); + await().atMost(5, TimeUnit.SECONDS).until(timer::isStarted); } verify(timer).startTimerSynchronized(); } @@ -57,7 +47,7 @@ void verifyActionCalled() { createSpyTimer(1, 10, action); timer.start(); - sleepAtLeast(50); + await().atMost(5, TimeUnit.SECONDS).until(timer::isStarted); verify(action, atLeastOnce()).getAsBoolean(); } @@ -67,7 +57,7 @@ void verifyActionResetsInactivity() { createSpyTimer(1, 10, () -> true); timer.start(); - sleepAtLeast(50); + await().atMost(5, TimeUnit.SECONDS).until(timer::isStarted); timer.start(); verify(timer).startTimerSynchronized(); @@ -81,7 +71,7 @@ void verifyRestartAfterInactivity() { } verify(timer).startTimerSynchronized(); - sleepAtLeast(150); + await().atMost(5, TimeUnit.SECONDS).until(timer::isStarted); for (int i = 0; i < 2; i++) { timer.start(); } From 82e8b1b09d9baf2141de07b3e147bfba7eff18c4 Mon Sep 17 00:00:00 2001 From: Marius Brill Date: Fri, 13 May 2022 13:41:12 +0200 Subject: [PATCH 83/86] Adjust timeout for HighPrecisionTimerTest --- .../ocelot/core/utils/HighPrecisionTimerTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java index 3c9a280fe7..a49d8b7aa0 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java @@ -33,7 +33,7 @@ void verifyStartResetsInactivity() { createSpyTimer(1, 100, () -> false); for (int i = 0; i < 10; i++) { timer.start(); - await().atMost(5, TimeUnit.SECONDS).until(timer::isStarted); + await().atMost(15, TimeUnit.SECONDS).until(timer::isStarted); } verify(timer).startTimerSynchronized(); } @@ -47,7 +47,7 @@ void verifyActionCalled() { createSpyTimer(1, 10, action); timer.start(); - await().atMost(5, TimeUnit.SECONDS).until(timer::isStarted); + await().atMost(15, TimeUnit.SECONDS).until(timer::isStarted); verify(action, atLeastOnce()).getAsBoolean(); } @@ -57,7 +57,7 @@ void verifyActionResetsInactivity() { createSpyTimer(1, 10, () -> true); timer.start(); - await().atMost(5, TimeUnit.SECONDS).until(timer::isStarted); + await().atMost(15, TimeUnit.SECONDS).until(timer::isStarted); timer.start(); verify(timer).startTimerSynchronized(); @@ -71,7 +71,7 @@ void verifyRestartAfterInactivity() { } verify(timer).startTimerSynchronized(); - await().atMost(5, TimeUnit.SECONDS).until(timer::isStarted); + await().atMost(15, TimeUnit.SECONDS).until(timer::isStarted); for (int i = 0; i < 2; i++) { timer.start(); } From 5b8c89704a41227ecaeb40162fd411b938eb51d6 Mon Sep 17 00:00:00 2001 From: Marius Oehler Date: Fri, 13 May 2022 15:25:30 +0200 Subject: [PATCH 84/86] refactored highprecisiontimer tests --- .../core/utils/HighPrecisionTimerTest.java | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java index a49d8b7aa0..64383ef75e 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/utils/HighPrecisionTimerTest.java @@ -6,10 +6,11 @@ import org.mockito.Mockito; import java.time.Duration; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; -import static org.awaitility.Awaitility.await; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; public class HighPrecisionTimerTest { @@ -25,6 +26,18 @@ void cleanUp() { timer.destroy(); } + private void sleepAtLeast(long ms) { + long start = System.nanoTime(); + while ((System.nanoTime() - start) < ms * 1000 * 1000) { + long slept = (System.nanoTime() - start) / 1000 / 1000; + try { + Thread.sleep(ms - slept); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + @Nested class Start { @@ -33,23 +46,26 @@ void verifyStartResetsInactivity() { createSpyTimer(1, 100, () -> false); for (int i = 0; i < 10; i++) { timer.start(); - await().atMost(15, TimeUnit.SECONDS).until(timer::isStarted); + sleepAtLeast(20); } verify(timer).startTimerSynchronized(); } @Test - void verifyActionCalled() { - BooleanSupplier action = mock(BooleanSupplier.class); - doReturn(true).when(action).getAsBoolean(); + void verifyActionCalled() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + BooleanSupplier action = () -> { + latch.countDown(); + return true; + }; createSpyTimer(1, 10, action); timer.start(); - await().atMost(15, TimeUnit.SECONDS).until(timer::isStarted); - verify(action, atLeastOnce()).getAsBoolean(); + boolean reachedZero = latch.await(5, TimeUnit.SECONDS); + assertThat(reachedZero).isTrue(); } @Test @@ -57,7 +73,7 @@ void verifyActionResetsInactivity() { createSpyTimer(1, 10, () -> true); timer.start(); - await().atMost(15, TimeUnit.SECONDS).until(timer::isStarted); + sleepAtLeast(50); timer.start(); verify(timer).startTimerSynchronized(); @@ -71,7 +87,7 @@ void verifyRestartAfterInactivity() { } verify(timer).startTimerSynchronized(); - await().atMost(15, TimeUnit.SECONDS).until(timer::isStarted); + sleepAtLeast(150); for (int i = 0; i < 2; i++) { timer.start(); } From ef3f1890e652eddb0d273a9812a86bf37701eb4c Mon Sep 17 00:00:00 2001 From: Marius Oehler Date: Fri, 13 May 2022 15:55:00 +0200 Subject: [PATCH 85/86] fixed breaking changes documentation --- .../docs/breaking-changes/breaking-changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md index ae5176e556..3830ccb704 100644 --- a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md +++ b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md @@ -2,7 +2,7 @@ id: Breaking Changes title: Breaking Changes --- -## Breaking changes in 2.X.X +## Breaking changes in 2.0.0 ### Integration of the OpenTelemetry OpenCensus Shim From 4a544e151b14e66931814fec25298f4f1bf164ed Mon Sep 17 00:00:00 2001 From: Marius Oehler Date: Fri, 13 May 2022 15:58:02 +0200 Subject: [PATCH 86/86] added autotracing availability info to the breaking changes --- .../docs/breaking-changes/breaking-changes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md index 3830ccb704..2c957eeea5 100644 --- a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md +++ b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md @@ -8,6 +8,11 @@ title: Breaking Changes Starting with the current release, inspectIT Ocelot migrates from OpenCensus to [OpenTelemetry](https://github.com/open-telemetry). As a first step, we include the [OpenTelemetry OpenCensus Shim](https://github.com/open-telemetry/opentelemetry-java/tree/main/opencensus-shim). inspectIT Ocelot still uses and supports the [OpenCensus-API](https://opencensus.io/quickstart/java/), but the exporter implementations of OpenTelemetry are used. +### AutoTracing currently not available + +Due to the migration from OpenCensus to OpenTelemetry, the agent's AutoTracing feature is currently **not** available. +The AutoTracing feature will be available again in the next release of the inspectIT Ocelot agent. + ### Updated and removed exporter #### Removed `OpenCensusAgentExporter`