Skip to content

Conversation

@AmilaSamith
Copy link
Contributor

@AmilaSamith AmilaSamith commented Dec 23, 2025

Purpose

This PR implements a span suppression mechanism to collapse all internal mediators within a connector operation into a single trace span, treating the entire connector execution as one atomic unit rather than exposing its internal implementation details.

Goals

Fixes: wso2/product-micro-integrator#3653

Approach

Problem:

Connectors (which are dynamic InvokeMediator templates) contain multiple internal mediators (property mediators, call mediators, class mediators, etc.), and by default each creates its own span, cluttering the trace with implementation details that users don't need to see.

Solution:

The PR introduces a SKIP_CHILD_EVENTS flag that controls span creation for child mediators:

  • Differentiate connectors from templates - InvokeMediator.getMediatorName() now appends ":Connector" for dynamic mediators and ":Template" for regular templates, allowing the statistics collectors to identify connector operations

  • Initialize skip flag - OpenEventCollector initializes SKIP_CHILD_EVENTS property to false on every message to ensure clean state

  • Activate suppression on connector entry - When OpenEventCollector detects an "InvokeMediator:Connector" component opening, it sets SKIP_CHILD_EVENTS = true, which signals that all subsequent child mediators should not create spans

  • Suppress child spans - In openFlowContinuableEvent() and openChildEntryEvent(), before calling the OpenTelemetry handler, the code checks if SKIP_CHILD_EVENTS is true and skips span creation if so, allowing only the connector's own span to be created

  • Deactivate suppression on connector exit - When CloseEventCollector detects "InvokeMediator:Connector" closing, it resets SKIP_CHILD_EVENTS = false, restoring normal span creation for subsequent mediators outside the connector

Traces after Changes

connector_single_unit

This ensures connector operations appear as clean, single-span units in traces (e.g., "OpenWeather_INTERNAL_ENDPOINT_REFERENCE" shows as one span without exposing the 20+ internal property mediators, call mediator, and class mediator underneath).

kafka_single_unit

Summary by CodeRabbit

  • Bug Fixes

    • Improved event handling logic for connector operations to prevent unnecessary child event emissions.
  • Refactor

    • Enhanced event collection filtering mechanism for OpenTelemetry tracing to better differentiate between connector and template mediator behaviors.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 23, 2025

Walkthrough

The changes implement a SKIP_CHILD_EVENTS control mechanism to consolidate connector invocations into single tracing spans. When processing an InvokeMediator:Connector, child events are suppressed during execution and re-enabled upon closure, enabling connectors to be reported as atomic units in OpenTelemetry.

Changes

Cohort / File(s) Summary
Event Collection Logic
OpenEventCollector.java, CloseEventCollector.java
Added SKIP_CHILD_EVENTS flag checks to suppress child event emissions for connector operations. OpenEventCollector filters entry, child entry, and flow-continuable events based on flag state. Special-case logic sets flag to true for "InvokeMediator:Connector" components. CloseEventCollector resets flag to false post-closure.
Statistics Constants
StatisticsConstants.java
Added public constant SKIP_CHILD_EVENTS = "SKIP_CHILD_EVENTS" for MessageContext property management.
Mediator Name Resolution
AbstractMediator.java, InvokeMediator.java
Updated AbstractMediator Javadoc for getMediatorName. Added getMediatorName override in InvokeMediator to append ":Connector" or ":Template" suffix based on isDynamicMediator state, enabling differentiation in telemetry.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant OpenEventCollector
    participant MessageContext
    participant CloseEventCollector
    
    Client->>OpenEventCollector: Process "InvokeMediator:Connector"<br/>(reportFlowContinuableEvent)
    OpenEventCollector->>MessageContext: Set SKIP_CHILD_EVENTS = true
    Note over OpenEventCollector,MessageContext: Connector span initiated<br/>(single unit)
    OpenEventCollector->>OpenEventCollector: Skip child event emissions<br/>(SKIP_CHILD_EVENTS=true)
    
    Note over Client: Connector mediators execute<br/>(events suppressed)
    
    Client->>CloseEventCollector: Connector closes<br/>(reportCloseEvent)
    CloseEventCollector->>MessageContext: Set SKIP_CHILD_EVENTS = false
    Note over CloseEventCollector,MessageContext: Connector span completed
    CloseEventCollector->>OpenEventCollector: Resume event emission<br/>(SKIP_CHILD_EVENTS=false)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A connector hops in, one span to trace,
No child events scattered all 'round the place!
When SKIP_CHILD_EVENTS flag takes its stand,
Tracing unites what the mediators planned.
One unit of work, crystal clear and neat—
Makes observability whole and complete! 🌟

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main feature: showing connector operations as a single span in traces, which matches the primary objective of the changeset.
Description check ✅ Passed The description provides Purpose, Goals, and detailed Approach sections that explain the span suppression mechanism, but lacks sections like Automation tests, Security checks, Test environment, and others from the template.
Linked Issues check ✅ Passed The PR fully addresses issue #3653 by implementing span suppression to treat connector invocations as single units, with differentiation of connectors via getMediatorName() suffix and SKIP_CHILD_EVENTS flag controlling child span creation.
Out of Scope Changes check ✅ Passed All changes are directly related to the span suppression mechanism: SKIP_CHILD_EVENTS constant, OpenEventCollector/CloseEventCollector logic, InvokeMediator.getMediatorName() differentiation, and supporting imports are all in-scope for the stated objective.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/collectors/CloseEventCollector.java (1)

126-131: Consider extracting the magic string to a constant.

The literal "InvokeMediator:Connector" is duplicated here and in OpenEventCollector. Extracting it to a constant in StatisticsConstants (e.g., CONNECTOR_MEDIATOR_NAME) would improve maintainability and ensure consistency.

🔎 Proposed refactor

In StatisticsConstants.java:

+public static final String CONNECTOR_MEDIATOR_NAME = "InvokeMediator:Connector";

Then update both collectors to use the constant:

-if (componentType == ComponentType.MEDIATOR && Objects.equals(componentName, "InvokeMediator:Connector")) {
+if (componentType == ComponentType.MEDIATOR && Objects.equals(componentName, StatisticsConstants.CONNECTOR_MEDIATOR_NAME)) {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64165d1 and be7f491.

📒 Files selected for processing (5)
  • modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/collectors/CloseEventCollector.java
  • modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/collectors/OpenEventCollector.java
  • modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/util/StatisticsConstants.java
  • modules/core/src/main/java/org/apache/synapse/mediators/AbstractMediator.java
  • modules/core/src/main/java/org/apache/synapse/mediators/template/InvokeMediator.java
🧰 Additional context used
🧬 Code graph analysis (2)
modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/collectors/CloseEventCollector.java (1)
modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/util/StatisticsConstants.java (1)
  • StatisticsConstants (26-204)
modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/collectors/OpenEventCollector.java (2)
modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/util/StatisticsConstants.java (1)
  • StatisticsConstants (26-204)
modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/tracing/opentelemetry/OpenTelemetryManagerHolder.java (1)
  • OpenTelemetryManagerHolder (31-87)
🔇 Additional comments (7)
modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/util/StatisticsConstants.java (1)

202-203: LGTM!

The new SKIP_CHILD_EVENTS constant follows the existing naming conventions and is appropriately placed with the other message context property keys. It serves as the coordination key for the connector span suppression mechanism.

modules/core/src/main/java/org/apache/synapse/mediators/AbstractMediator.java (1)

478-483: LGTM!

Minor Javadoc improvement that clarifies the return value of getMediatorName(). This provides useful context given that InvokeMediator now overrides this method with custom naming logic.

modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/collectors/CloseEventCollector.java (1)

119-132: Verify: SKIP_CHILD_EVENTS is not reset when a connector closes with an error.

The flag reset only occurs in the non-error branch. If a connector closes with an error (isError == true), subsequent mediators in any fault handling sequence may not create spans. Please confirm this is the intended behavior.

modules/core/src/main/java/org/apache/synapse/mediators/template/InvokeMediator.java (1)

281-285: LGTM!

The override clearly differentiates connectors from templates by appending the appropriate suffix. This enables the statistics collectors to identify connector operations and apply the span suppression logic. The isDynamicMediator() check is the correct discriminator.

modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/collectors/OpenEventCollector.java (3)

66-69: LGTM!

Good defensive initialization of SKIP_CHILD_EVENTS to false at entry points. This ensures the flag has a defined state at the start of each message flow.


217-237: Verify: reportFlowSplittingEvent does not respect SKIP_CHILD_EVENTS.

Flow splitting mediators (Clone, Iterate) will create spans even when inside a connector operation. If a connector uses these mediators internally, they would not be suppressed. Please confirm this is intentional behavior, or if these should also check SKIP_CHILD_EVENTS.


191-195: LGTM on the connector identification logic.

The special-case handling correctly identifies InvokeMediator:Connector and sets the SKIP_CHILD_EVENTS flag to suppress child spans. This is the core mechanism enabling connectors to appear as single spans.

Note: The magic string "InvokeMediator:Connector" is also duplicated in CloseEventCollector.java — see the earlier suggestion to extract it to a constant.

Comment on lines +113 to 117
if (!isFiltered && isOpenTelemetryEnabled()
&& messageContext.getProperty(StatisticsConstants.SKIP_CHILD_EVENTS).equals(false)) {
OpenTelemetryManagerHolder.getOpenTelemetryManager().getHandler()
.handleOpenEntryEvent(statisticDataUnit, messageContext);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential NPE if SKIP_CHILD_EVENTS is unexpectedly null.

Line 114 calls .equals(false) directly on the property value without a null check. While the initialization at lines 67-69 should prevent this in normal flows, the pattern differs from the safer null-checks used in reportChildEntryEvent and reportFlowContinuableEvent.

🔎 Proposed fix for consistency
             boolean isFiltered = isSpanFilteredMediator(componentName, aspectConfiguration);
-            if (!isFiltered && isOpenTelemetryEnabled()
-                    && messageContext.getProperty(StatisticsConstants.SKIP_CHILD_EVENTS).equals(false)) {
+            boolean skipChildEvents = Boolean.TRUE.equals(
+                    messageContext.getProperty(StatisticsConstants.SKIP_CHILD_EVENTS));
+            if (!isFiltered && isOpenTelemetryEnabled() && !skipChildEvents) {
                 OpenTelemetryManagerHolder.getOpenTelemetryManager().getHandler()
                             .handleOpenEntryEvent(statisticDataUnit, messageContext);
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!isFiltered && isOpenTelemetryEnabled()
&& messageContext.getProperty(StatisticsConstants.SKIP_CHILD_EVENTS).equals(false)) {
OpenTelemetryManagerHolder.getOpenTelemetryManager().getHandler()
.handleOpenEntryEvent(statisticDataUnit, messageContext);
}
boolean isFiltered = isSpanFilteredMediator(componentName, aspectConfiguration);
boolean skipChildEvents = Boolean.TRUE.equals(
messageContext.getProperty(StatisticsConstants.SKIP_CHILD_EVENTS));
if (!isFiltered && isOpenTelemetryEnabled() && !skipChildEvents) {
OpenTelemetryManagerHolder.getOpenTelemetryManager().getHandler()
.handleOpenEntryEvent(statisticDataUnit, messageContext);
}
🤖 Prompt for AI Agents
In
modules/core/src/main/java/org/apache/synapse/aspects/flow/statistics/collectors/OpenEventCollector.java
around lines 113 to 117, the condition calls
messageContext.getProperty(StatisticsConstants.SKIP_CHILD_EVENTS).equals(false)
which can NPE if the property is null; change the check to use a null-safe
Boolean comparison (e.g.,
Boolean.FALSE.equals(messageContext.getProperty(StatisticsConstants.SKIP_CHILD_EVENTS)))
or explicitly test for null before equals so the overall if remains: if
(!isFiltered && isOpenTelemetryEnabled() &&
Boolean.FALSE.equals(messageContext.getProperty(StatisticsConstants.SKIP_CHILD_EVENTS)))
then call handleOpenEntryEvent, matching the null-safety used in
reportChildEntryEvent and reportFlowContinuableEvent.

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.

[Improvement]: Consider connector invocations as a single unit for tracing purposes

1 participant