Skip to content

fix(config): idp-core observability - #168

Open
foukou19 wants to merge 31 commits into
mainfrom
feat/idp-core-observability
Open

foukou19 wants to merge 31 commits into
mainfrom
feat/idp-core-observability

Conversation

@foukou19

@foukou19 foukou19 commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

fix(metrics,logging,docs): add and harden webhook custom metrics with OTel runtime alignment

What this PR Provides

  • Migrates observability configuration from legacy management.* tracing/export keys to native otel.* runtime keys.

  • Keeps Camel tracing through camel-opentelemetry2-starter and the Camel/OpenTelemetry bridge.

  • Adds and stabilizes webhook custom metrics (idp.webhook.*) emitted through the native OpenTelemetry Meter.

  • Aligns observability documentation with emitted metric labels, available tests, and effective runtime behavior.

  • ADR link: N/A

Fixes

  • N/A

Review

The reviewer must double-check these points:

  • The reviewer has tested the feature
  • The reviewer has reviewed the implementation of the feature
  • The documentation has been updated
  • The feature implementation respects the Technical Doc / ADR previously produced
  • The Pull Request title has a ! after the type/scope to identify the breaking
    change in the release note and ensure we will release a major version.

Breaking changes (if any)

  • N/A

foukou19 and others added 10 commits October 6, 2026 11:32
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
Co-authored-by: Eve Bernhard <115796475+evebrnd@users.noreply.github.com>
Signed-off-by: ferial OUKOUKAS <75682459+foukou19@users.noreply.github.com>
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
@foukou19 foukou19 changed the title Feat/idp core observability feat(config): idp-core observability Oct 13, 2026
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
@foukou19
foukou19 force-pushed the feat/idp-core-observability branch from 3229034 to 4bad155 Compare October 13, 2026 13:48
@github-code-quality

github-code-quality Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Code coverage overview

Languages: Java

Java / code-coverage/jacoco

main 017c4b0 feat/idp-core-observ... 8f47512 +/-
91% 91% 0%
View coverage of the 10 most impacted files.
File main 017c4b0 feat/idp-core-observ... 8f47512 +/-
com/decathlon/i...nProcessor.java 98% 89% -9%
com/decathlon/i...yProcessor.java 100% 98% -2%
com/decathlon/i...ationUtils.java 87% 86% -1%
com/decathlon/i...Controller.java 100% 100% 0%
com/decathlon/i...gProcessor.java 81% 82% +1%
com/decathlon/i...uteBuilder.java 94% 96% +2%
com/decathlon/i...itySupport.java 0% 91% +91%
com/decathlon/i...ebugConfig.java 0% 92% +92%
com/decathlon/i...figuration.java 0% 100% +100%
com/decathlon/i...figuration.java 0% 100% +100%

ℹ️ Calculations represent line coverage. Results may change as calculations are updated.


Updated November 13, 2026 14:21 UTC

foukou19 and others added 4 commits October 14, 2026 16:55
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
Signed-off-by: ferial OUKOUKAS <75682459+foukou19@users.noreply.github.com>
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>

Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved metric accuracy and label-cardinality issues undermine reliable monitoring.

5 open findings
What changed in this PR

Adds OpenTelemetry metrics to idp-core’s webhook ingestion pipeline.

Changes:

  • Instruments security validation, payload decoding, mapping, and entity operations.
  • Adds shared metric constants, helpers, and meter configuration.
  • Updates test setup and telemetry dependencies.
File Description
src/​test/​java/​com/​decathlon/​idp_core/​infrastructure/​adapters/​ingestion/​processor/​IngestionProcessorTest.java Supplies a test meter.
src/​test/​java/​com/​decathlon/​idp_core/​infrastructure/​adapters/​ingestion/​processor/​DecodingProcessorTest.java Updates constructors with a meter.
src/​main/​java/​com/​decathlon/​idp_core/​infrastructure/​adapters/​ingestion/​processor/​SecurityProcessor.java Records security outcomes.
src/​main/​java/​com/​decathlon/​idp_core/​infrastructure/​adapters/​ingestion/​processor/​IngestionProcessor.java Records mapping and entity metrics.
src/​main/​java/​com/​decathlon/​idp_core/​infrastructure/​adapters/​ingestion/​processor/​decoder/​DecodingProcessor.java Records decoding outcomes and payload sizes.
src/​main/​java/​com/​decathlon/​idp_core/​infrastructure/​adapters/​ingestion/​metrics/​IngestionObservabilitySupport.java Adds shared instrumentation helpers.
src/​main/​java/​com/​decathlon/​idp_core/​infrastructure/​adapters/​ingestion/​configuration/​IngestionObservabilityConstants.java Defines metric names and attributes.
src/​main/​java/​com/​decathlon/​idp_core/​infrastructure/​adapters/​ingestion/​configuration/​IngestionMetricsConfiguration.java Configures scoped meters.
pom.xml Adds telemetry API and testing dependencies.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@foukou19
foukou19 force-pushed the feat/idp-core-observability branch 4 times, most recently from 072443b to 33e8861 Compare October 16, 2026 10:04
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
@foukou19
foukou19 force-pushed the feat/idp-core-observability branch from 33e8861 to d55eb94 Compare October 16, 2026 13:51
@foukou19
foukou19 force-pushed the feat/idp-core-observability branch from 93cdcad to 1226447 Compare November 4, 2026 16:33
@foukou19
foukou19 requested a balanced review from Copilot November 4, 2026 16:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment on lines +231 to +236
metrics:
endpoint: ${OTEL_EXPORTER_OTLP_METRICS_ENDPOINT:${OTEL_EXPORTER_OTLP_ENDPOINT:http://localhost:4318}/v1/metrics}
protocol: ${OTEL_EXPORTER_OTLP_METRICS_PROTOCOL:${OTEL_EXPORTER_OTLP_PROTOCOL:http/protobuf}}
traces:
endpoint: ${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:${OTEL_EXPORTER_OTLP_ENDPOINT:http://localhost:4318}/v1/traces}
protocol: ${OTEL_EXPORTER_OTLP_TRACES_PROTOCOL:${OTEL_EXPORTER_OTLP_PROTOCOL:http/protobuf}}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@foukou19 to check

Comment thread src/test/resources/application-test.yml Resolved
Comment thread docs/src/deployment/observability.md Resolved
Comment thread docs/src/deployment/observability.md Resolved
Comment thread docs/src/deployment/observability.md Resolved
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@foukou19
foukou19 requested a balanced review from Copilot November 5, 2026 13:48
@foukou19
foukou19 force-pushed the feat/idp-core-observability branch from 169d699 to b8ec7cb Compare November 5, 2026 13:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

…s export

Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
@foukou19
foukou19 force-pushed the feat/idp-core-observability branch 2 times, most recently from a4c5b0e to 59bc7d0 Compare November 5, 2026 15:31
@foukou19
foukou19 requested a balanced review from Copilot November 5, 2026 15:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@foukou19
foukou19 force-pushed the feat/idp-core-observability branch 2 times, most recently from 1333d0f to 988541a Compare November 5, 2026 16:04
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
@foukou19
foukou19 force-pushed the feat/idp-core-observability branch from 988541a to 00b757a Compare November 5, 2026 16:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment on lines +221 to +223
incrementCounter(IngestionObservabilityConstants.FUNCTIONAL_VALIDATION_FAILED_TOTAL,
ATTRIBUTE_CONNECTOR, connectorIdentifier, ATTRIBUTE_TEMPLATE, templateIdentifier,
ATTRIBUTE_REASON, exception.getClass().getSimpleName());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@foukou19 to check

max-page-size: ${MAX_PAGE_SIZE:500}
ingestion:
# Enable temporary diagnostics to inspect Camel exchange telemetry context propagation.
tracing-diagnostic-enabled: ${APP_INGESTION_TRACING_DIAGNOSTIC_ENABLED:true}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@foukou19 to check

Comment on lines +241 to +242
trace-id-key: traceId
span-id-key: spanId

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@foukou19 to check

is ignored. Set `OTEL_TRACES_SAMPLER` to a ratio-based sampler if you want the
argument to control sampling.

Replace the old `OTEL_TRACING_ENABLED` and `OTEL_METRICS_EXPORT_ENABLED` flags with exporter selection (`otlp` or `none`). Replace `*_TRANSPORT=http` with `*_PROTOCOL=http/protobuf`. The official starter reads `otel.*`, not the former `management.opentelemetry.*` export settings. The test profile disables network export for all signals.

@brandPittCode brandPittCode left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Test coverage does not guard the regression fixed by this PR

Currently only 3 mapping tests in IngestionProcessorTest.java:173-253 assert metric output.

Add an autoconfiguration test verifying that the Spring context wires a non-noop Meter (preventing regressions back to DefaultMeter), and add InMemoryMetricReader / span assertions for:

Entity lifecycle & failure counters (ENTITY_CREATED_TOTAL, ENTITY_UPDATED_TOTAL, ENTITY_PATCH_TOTAL, ENTITY_DELETED_TOTAL, ENTITY_NOT_FOUND_TOTAL, ENTITY_CONFLICT_TOTAL, ENTITY_VALIDATION_ERRORS_TOTAL, FUNCTIONAL_VALIDATION_FAILED_TOTAL).
Security counters and span attributes in SecurityProcessor.java:46-93 (currently no dedicated SecurityProcessorTest exists).
Payload size, decoded, and decode-error metrics in DecodingProcessorTest.java:28-41.


@Configuration
@Slf4j
public class CamelTracingConfiguration {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Add class markdown JavaDoc

import io.opentelemetry.api.metrics.Meter;

@Configuration
public class IngestionMetricsConfiguration {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Add class markdown JavaDoc

import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.trace.Span;

public final class IngestionObservabilitySupport {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Add class and methods markdown JavaDoc

Comment on lines +221 to +223
incrementCounter(IngestionObservabilityConstants.FUNCTIONAL_VALIDATION_FAILED_TOTAL,
ATTRIBUTE_CONNECTOR, connectorIdentifier, ATTRIBUTE_TEMPLATE, templateIdentifier,
ATTRIBUTE_REASON, exception.getClass().getSimpleName());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@foukou19 to check

Span.current().setAttribute(SPAN_ATTRIBUTE_SECURITY_RESULT, "REJECTED");
String result = isSecurityRejected(exception) ? RESULT_REJECTED : RESULT_ERROR;
recordSecurityResult(webhookIdentifier, securityType, result);
incrementCounter(IngestionObservabilityConstants.SECURITY_VALIDATION_ERRORS_TOTAL,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe add more specific name. Like incrementSecuritityValidationErrorsCounter

org.apache.camel.opentelemetry: ${CAMEL_OTEL_DIAGNOSTIC_LOG_LEVEL:WARN}
io.opentelemetry.exporter: ${OTEL_EXPORTER_DIAGNOSTIC_LOG_LEVEL:WARN}
io.opentelemetry.exporter.sender.okhttp: ${OTEL_EXPORTER_HTTP_DIAGNOSTIC_LOG_LEVEL:WARN}
khttp3: ${OKHTTP_DIAGNOSTIC_LOG_LEVEL:WARN}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

khttp3 is missing the leading o (okhttp3), so OKHTTP_DIAGNOSTIC_LOG_LEVEL never takes effect.


static final String CAMEL_INSTRUMENTATION_SCOPE = "camel";

@Bean

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Null-safe OpenTelemetry fallback in Camel tracing

In CamelTracingConfiguration.java:20-29, pass OpenTelemetry::noop as the default supplier so sliced tests or contexts without the OTel starter do not fail with a NullPointerException:

Suggested change
@Bean
@Bean
public Tracer camelTracer(ObjectProvider<OpenTelemetry> openTelemetryProvider) {
return openTelemetryProvider.getIfAvailable(OpenTelemetry::noop)
.getTracer(CAMEL_INSTRUMENTATION_SCOPE);
}
@Bean
public ContextPropagators camelContextPropagators(
ObjectProvider<OpenTelemetry> openTelemetryProvider) {
return openTelemetryProvider.getIfAvailable(OpenTelemetry::noop).getPropagators();
}


public final class IngestionObservabilitySupport {

private static final Map<InstrumentCacheKey, LongCounter> COUNTER_CACHE = new ConcurrentHashMap<>();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Static instrument cache retains closed Meter instances

IngestionObservabilitySupport.java:24-25 stores counters and histograms in static final ConcurrentHashMaps keyed by Meter identity. Every new SdkMeterProvider created in unit tests or context reloads remains strongly referenced for the JVM lifetime.

Because IngestionProcessor, SecurityProcessor, and DecodingProcessor are singleton @components, building and storing their LongCounter / LongHistogram instruments as instance fields in the constructor removes the static leak, runtime cache lookups, and per-event String... allocations—and lets you attach .setUnit(...) and .setDescription(...) metadata (ms, By).

Replace static Meter caches with constructor-owned instruments
Instead of caching instruments in static final ConcurrentHashMaps inside IngestionObservabilitySupport.java:24-45, build the instruments once in each singleton processor's constructor (with units and descriptions) and keep IngestionObservabilitySupport stateless.

Example for SecurityProcessor.java:37-44:

private final List<WebhookSecurityStrategy> strategies;
private final LongCounter securityRequestsCounter;
private final LongCounter securityResultCounter;
private final LongCounter securityValidationErrorsCounter;

public SecurityProcessor(List<WebhookSecurityStrategy> strategies,
    @Qualifier("securityMeter") Meter meter) {
  this.strategies = List.copyOf(strategies);
  this.securityRequestsCounter = meter.counterBuilder(SECURITY_REQUESTS_TOTAL)
      .setDescription("Number of security checks executed for a connector")
      .build();
  this.securityResultCounter = meter.counterBuilder(SECURITY_RESULT)
      .setDescription("Result of the webhook security decision")
      .build();
  this.securityValidationErrorsCounter = meter.counterBuilder(SECURITY_VALIDATION_ERRORS_TOTAL)
      .setDescription("Security validation failures and malformed configuration errors")
      .build();
}

And recording measurements directly with typed AttributeKeys avoids per-request String... vararg parsing and cache lookups:

securityRequestsCounter.add(1, Attributes.of( CONNECTOR_KEY, webhookIdentifier, SECURITY_TYPE_KEY, securityType));

(Apply the same constructor pattern to DecodingProcessor with .setUnit("By") on PAYLOAD_SIZE_BYTES, and remove COUNTER_CACHE, HISTOGRAM_CACHE, and InstrumentCacheKey from IngestionObservabilitySupport.java:24-133.)

public static final String ENTITY_NOT_FOUND_TOTAL = "idp.webhook.entity.notfound.total";
public static final String ENTITY_CONFLICT_TOTAL = "idp.webhook.entity.conflict.total";

public static final String ATTRIBUTE_CONNECTOR = "connector";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Duplicated constants and raw attribute keys: SPAN_ATTRIBUTE_WEBHOOK_IDENTIFIER is defined in IngestionObservabilityConstants.java:37 and redeclared privately in both SecurityProcessor.java:31 and IngestionProcessor.java:33. Keys like "error_type" and "encoding" are also inline literals rather than shared constants.

Centralize constants and typed AttributeKeys
Update IngestionObservabilityConstants.java:29-38 so processors do not redeclare SPAN_ATTRIBUTE_* or inline "encoding" / "error_type":

public static final String ATTRIBUTE_CONNECTOR = "connector";
public static final String ATTRIBUTE_MAPPING_ID = "mapping_id";
public static final String ATTRIBUTE_ACTION = "action";
public static final String ATTRIBUTE_REASON = "reason";
public static final String ATTRIBUTE_TEMPLATE = "template";
public static final String ATTRIBUTE_RESULT = "result";
public static final String ATTRIBUTE_SECURITY_TYPE = "security_type";
public static final String ATTRIBUTE_ENCODING = "encoding";
public static final String ATTRIBUTE_ERROR_TYPE = "error_type";

public static final AttributeKey<String> CONNECTOR_KEY = AttributeKey.stringKey(ATTRIBUTE_CONNECTOR);
public static final AttributeKey<String> MAPPING_ID_KEY = AttributeKey.stringKey(ATTRIBUTE_MAPPING_ID);
public static final AttributeKey<String> ACTION_KEY = AttributeKey.stringKey(ATTRIBUTE_ACTION);
public static final AttributeKey<String> REASON_KEY = AttributeKey.stringKey(ATTRIBUTE_REASON);
public static final AttributeKey<String> TEMPLATE_KEY = AttributeKey.stringKey(ATTRIBUTE_TEMPLATE);
public static final AttributeKey<String> RESULT_KEY = AttributeKey.stringKey(ATTRIBUTE_RESULT);
public static final AttributeKey<String> SECURITY_TYPE_KEY = AttributeKey.stringKey(ATTRIBUTE_SECURITY_TYPE);
public static final AttributeKey<String> ENCODING_KEY = AttributeKey.stringKey(ATTRIBUTE_ENCODING);
public static final AttributeKey<String> ERROR_TYPE_KEY = AttributeKey.stringKey(ATTRIBUTE_ERROR_TYPE);

public static final String SPAN_ATTRIBUTE_WEBHOOK_IDENTIFIER = "idp.webhook.identifier";
public static final String SPAN_ATTRIBUTE_SECURITY_RESULT = "idp.webhook.security.result";

Then delete the duplicate private static final String SPAN_ATTRIBUTE_* fields in SecurityProcessor.java:31-32 and IngestionProcessor.java:33.

log.debug("Mapping filter excluded payload for template: {}",
mapping.entityTemplateIdentifier());
return;
private void applyMapping(String payload, EntityDynamicMapping mapping,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Fix unsupported action outcome, ENTITY_NOT_FOUND_TOTAL action label, and sub-ms latency in IngestionProcessor

Build a DoubleHistogram (.setUnit("ms")) instead of LongHistogram so sub-millisecond mappings are not truncated to 0 (MetricData.getHistogramData() in IngestionProcessorTest.java:148-154 continues to work unchanged).
Throw on case null, default so an invalid action is recorded as ERROR in the catch block rather than falling through to MATCHED.
Pass action to recordEntityFailureMetrics instead of hardcoding "ingestion".

@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants