[ 
https://issues.apache.org/jira/browse/NIP-30?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Kevin Doran updated NIP-30:
---------------------------
    Description: 
h2. Motivation

The Connector extension point introduced by NIP-11 and implemented under 
NIFI-15258 is a new top-level component whose flow lives inside a managed 
Process Group that is a sibling of the controller's root Process Group, not a 
descendant. Two observability gaps follow from that placement:
 # Logs emitted by components inside a connector-managed flow have no way to 
carry connector identity in MDC. NiFi already provides MDC context for 
components in the controller's root flow via the {{processGroup*}} keys, but 
those keys describe the containing PG hierarchy, not the owning Connector. 
There is also no mechanism for a runtime operator to publish additional 
identity-shaped MDC attributes (for example, a connector definition identifier 
known to the runtime's connector configuration system) for the components 
running inside a connector-managed flow.
 # Reporting tasks that walk {{EventAccess.getControllerStatus()}} cannot see 
connector-managed flows at all. Because connector-managed PGs are siblings of 
the root, the existing controller traversal misses them entirely; built-in and 
third-party reporting tasks therefore export zero metrics for connector flows 
today.

This NIP proposes a small, additive set of API changes across {{nifi-api}} and 
{{nifi-framework-api}} to close both gaps without affecting existing 
components, callers, or test stubs. The framework wiring that supplies behavior 
behind these defaults is out of scope and will be tracked in a separate NIFI 
Jira.
h2. Scope

Four additions and one field, all backward compatible by virtue of default 
methods, default field values, and no removed/renamed members:
 # New default method on 
{{org.apache.nifi.components.connector.ConnectorConfigurationProvider}} 
({{{}nifi-framework-api{}}}):
{{default Map<String, String> getLoggingAttributes(String connectorId) {    
return Map.of();
}}}
 # New {{loggingAttributes}} field on {{ProcessGroupStatus}} ({{{}Map<String, 
String>{}}}, defaults to {{{}Map.of(){}}}) with accessors that take a defensive 
immutable copy on set; included in {{clone()}} and in the static {{merge()}} 
helper (additive/union merge across cluster nodes).
 # New status type {{org.apache.nifi.controller.status.ConnectorStatus}} 
carrying connector identity ({{{}id{}}}, {{{}name{}}}) and the 
{{ProcessGroupStatus}} for the Connector's managed root Process Group 
({{{}rootGroupStatus{}}}). Implements {{Cloneable}} and follows the established 
Status-class conventions ({{{}PortStatus{}}}, {{{}ConnectionStatus{}}}, etc.). 
Positioned to grow with connector-level metrics that have no Process-Group 
analog (for example, connector-wide backlog or idle time) in subsequent work.
 # New default method on {{{}EventAccess{}}}:
{{default Collection<ConnectorStatus> getConnectorStatuses() {    return 
Collections.emptyList();
}}}

No existing types, methods, or fields are altered. No new compile-time 
dependencies are introduced; consumer-side imports are limited to 
{{org.apache.nifi.controller.status.ConnectorStatus}} ({{{}nifi-api{}}}) and 
the new method on the existing {{ConnectorConfigurationProvider}} 
({{{}nifi-framework-api{}}}).
h2. Description
h3. {{ConnectorConfigurationProvider.getLoggingAttributes(String)}}

{{ConnectorConfigurationProvider}} is a {{nifi-framework-api}} extension point 
already used by external configuration systems. A provider implementation has 
the inventory needed to map a connector identifier to identity-shaped 
attributes that originate outside the connector's own NAR — for example, an 
externally-issued connector definition identifier that all instances of a given 
connector type share.

The framework consults this method once, at connector create time (within 
{{{}ExtensionBuilder.buildConnector{}}}), and merges the returned map into the 
MDC context that wraps the connector's {{ComponentLog}} and the loggers of 
every component (Processor, Controller Service, etc.) running inside the 
Connector's managed flow. The framework reserves a small set of well-known keys 
that describe the Connector's own identity ({{{}connectorId{}}}, 
{{{}connectorName{}}}, {{{}connectorComponent{}}}, 
{{{}connectorBundleGroup{}}}, {{{}connectorBundleArtifact{}}}, 
{{{}connectorBundleVersion{}}}); attempts to override these via the returned 
map are dropped and the framework logs a {{WARN}} for each rejected key. The 
exact set of reserved keys is a framework concern and is intentionally not 
specified in {{{}nifi-framework-api{}}}.

The default returns {{Map.of()}} so that:
 * Apache NiFi deployments (no provider configured) get the framework-managed 
identity keys and nothing else.
 * Existing provider implementations compile unchanged and contribute no 
additional MDC until they opt in.

Why a provider method instead of a connector-side push:
 * The trust boundary stays at the runtime operator, not the NAR author. Only 
the runtime's chosen {{ConnectorConfigurationProvider}} can publish MDC; 
arbitrary connector NARs cannot.
 * The provider already owns the connector-identifier-to-external-configuration 
mapping that the new attribute values come from; surfacing them is reading an 
existing source of truth rather than threading a new one through the API.
 * Mid-lifecycle refresh of these attributes is not currently in scope; a 
static-at-create model is sufficient for the identifier use case driving this 
NIP, and a refresh hook can be added later without breaking the default.

h3. {{ProcessGroupStatus.loggingAttributes}}

When a reporting task observes a {{{}ProcessGroupStatus{}}}, it now also sees a 
snapshot of the logging attributes that apply to that Process Group at capture 
time. For PGs inside a connector-managed flow this carries the 
connector-identity keys (both framework-managed and provider-supplied); for 
ordinary PGs it carries the existing {{processGroup*}} keys. Reporting tasks 
that export to systems like OpenTelemetry can then attach those keys as metric 
attributes without any additional traversal.

Setter takes a defensive immutable copy ({{{}Map.copyOf{}}}); null is 
normalized to {{{}Map.of(){}}}. {{clone()}} routes through the setter so the 
cloned instance always holds an independent immutable map. {{merge()}} is 
additive (union with last-write-wins per key), which keeps the behavior 
forgiving when cluster nodes are mid-deploy and one node has not yet populated 
its map.
h3. {{ConnectorStatus}}

A connector-level status object analogous to {{{}ProcessGroupStatus{}}}. The 
minimum viable fields are the connector's identity and the status of its 
managed root PG. It is introduced now (rather than reusing 
{{ProcessGroupStatus}} directly from 
{{{}EventAccess.getConnectorStatuses(){}}}) because:
 * Reporting tasks need to identify the connector independently of the root 
PG's id, which may not match the connector id.
 * Connectors have observable state that does not exist at the PG level (e.g. 
connector-wide backlog, idle time, lifecycle state). Future NIPs can add fields 
here without re-shaping the {{EventAccess}} return type.

h3. {{EventAccess.getConnectorStatuses()}}

A new entry point for reporting tasks to enumerate connector-managed flows 
alongside the controller's root flow. Implementations walk the runtime's 
Connector registry, build a {{ConnectorStatus}} for each (populated with the 
existing {{getGroupStatus(...)}} result for the managed root), and return them. 
The default returns an empty list so that:
 * Existing {{EventAccess}} implementations (tests, mocks, downstream 
frameworks) compile unchanged.
 * Existing reporting tasks that do not call this method see no behavior change.

The framework implementation of this method, and the changes required in 
built-in reporting tasks to consume it, are out of scope for this NIP and will 
be tracked under a separate NIFI Jira.
h2. Compatibility

Purely additive:
 * All new interface methods have default implementations ({{{}Map.of(){}}} for 
{{{}getLoggingAttributes{}}}, empty collection for 
{{{}getConnectorStatuses{}}}).
 * The new {{loggingAttributes}} field on {{ProcessGroupStatus}} defaults to 
{{{}Map.of(){}}}; existing serialization round-trips of {{ProcessGroupStatus}} 
are unaffected because the field is not currently present in any persisted 
form. Existing reflective consumers see a new no-arg accessor and an empty 
default.
 * {{ConnectorStatus}} is a brand-new type; nothing references it today.
 * No method signatures, return types, or behaviors are altered.

The Connector API is already marked experimental per NIP-11, but these 
particular additions are designed to be safe to lift into a stable form without 
further changes.
h2. Verification
 * Existing unit tests across {{nifi-api}} and {{nifi-framework-api}} continue 
to compile and pass without modification.
 * New unit tests cover:
 ** {{ProcessGroupStatus.setLoggingAttributes}} defensive copy (mutating the 
caller's map after set does not mutate the stored map), {{clone()}} 
independence, and {{merge()}} union semantics across overlapping/disjoint key 
sets.
 ** {{ConnectorStatus.clone()}} independence (mutating the source 
{{rootGroupStatus}} does not affect the clone).
 ** {{EventAccess.getConnectorStatuses()}} default returns an empty, immutable 
collection.
 ** {{ConnectorConfigurationProvider.getLoggingAttributes(String)}} default 
returns an empty, immutable map.
 * Framework-level integration (the framework call into the provider, 
reserved-key filtering, MDC propagation, and {{EventAccess}} population) is 
verified when the corresponding NiFi framework changes consume the new methods 
(tracked separately).

h2. Alternatives
 # Use {{ProcessGroupStatus}} directly from 
{{EventAccess.getConnectorStatuses()}} (no new type). Rejected because (a) the 
connector's identity is not necessarily the root PG's identity, and (b) 
Connectors are expected to grow connector-level observable state (backlog, idle 
time, lifecycle) that does not fit on {{ProcessGroupStatus}} — introducing the 
wrapper now avoids a return-type breaking change later.
 # Surface custom MDC attributes via a connector-side push on 
{{ConnectorInitializationContext}} (e.g. {{setLoggingAttributes(Map)}} invoked 
from inside {{{}Connector.initialize(...){}}}). Rejected because (a) it places 
the trust boundary at the NAR author rather than at the framework level, 
allowing any third-party connector to inject arbitrary MDC keys that downstream 
observability pipelines would interpret as first-class identity, and (b) the 
static-at-create model is sufficient for the identifier use case driving this 
NIP — dynamic-from-config attributes can be added later as a refresh hook on 
the provider without breaking the API. The provider-side pull does not preclude 
a connector-side push being added later if a use case emerges.
 # Extend {{EventAccess.getControllerStatus()}} to recursively include 
connector-managed flows. Rejected because connector-managed flows are 
intentionally siblings of the root group (per NIP-11); merging them under the 
root would conflate two distinct hierarchies and break existing reporting-task 
assumptions.
 # Promote logging attributes via the existing bulletin/event mechanism rather 
than via a snapshot field on {{{}ProcessGroupStatus{}}}. Rejected because the 
snapshot field is the lowest-friction integration point for the OpenTelemetry 
reporting task and other status-based exporters that already iterate 
{{{}ProcessGroupStatus{}}}.

  was:
h2. Motivation

The Connector extension point introduced by NIP-11 and implemented under 
NIFI-15258 is a new top-level component whose flow lives inside a managed 
Process Group that is a sibling of the controller's root Process Group, not a 
descendant. Two observability gaps follow from that placement:
 # Logs emitted by components inside a connector-managed flow have no way to 
carry connector identity in MDC. NiFi already provides MDC context for 
components in the controller's root flow via the {{processGroup*}} keys, but 
those keys describe the containing PG hierarchy, not the owning Connector. A 
Connector implementation also has no API to publish its own identity-shaped MDC 
attributes (for example, a connector definition identifier) for the components 
running inside its flow.
 # Reporting tasks that walk {{EventAccess.getControllerStatus()}} cannot see 
connector-managed flows at all. Because connector-managed PGs are siblings of 
the root, the existing controller traversal misses them entirely; built-in and 
third-party reporting tasks therefore export zero metrics for connector flows 
today.

This NIP proposes a small, additive set of API changes in {{nifi-api}} to close 
both gaps without affecting existing components, callers, or test stubs. The 
framework wiring that supplies behavior behind these defaults is out of scope 
and will be tracked in a separate NIFI Jira.
h2. Scope

Three additions and one field, all in {{{}nifi-api{}}}, all backward compatible 
by virtue of {{default}} methods, default field values, and no removed/renamed 
members:
 # New default method on {{{}ConnectorInitializationContext{}}}:
default void setLoggingAttributes(Map<String, String> attributes) {
thrownewUnsupportedOperationException();
}
 # New {{loggingAttributes}} field on {{ProcessGroupStatus}} ({{{}Map<String, 
String>{}}}, defaults to {{{}Map.of(){}}}) with accessors that take a defensive 
immutable copy on set; included in {{clone()}} and in the static {{merge()}} 
helper (additive/union merge across cluster nodes).

 # New status type {{org.apache.nifi.controller.status.ConnectorStatus}} 
carrying connector identity ({{{}id{}}}, {{{}name{}}}) and the 
{{ProcessGroupStatus}} for the Connector's managed root Process Group 
({{{}rootGroupStatus{}}}). Implements {{Cloneable}} and follows the established 
Status-class conventions ({{{}PortStatus{}}}, {{{}ConnectionStatus{}}}, etc.). 
Positioned to grow with connector-level metrics that have no Process-Group 
analog (for example, connector-wide backlog or idle time) in subsequent work.

 # New default method on {{{}EventAccess{}}}:
default Collection<ConnectorStatus> getConnectorStatuses() {
return Collections.emptyList();
}

No existing types, methods, or fields are altered. No new compile-time 
dependencies are introduced; the only new import on the consumer side is 
{{{}org.apache.nifi.controller.status.ConnectorStatus{}}}.
h2. Description
h3. {{ConnectorInitializationContext.setLoggingAttributes(Map)}}

A Connector implementation invokes this from within its {{initialize(...)}} 
lifecycle (or at any later point, e.g. once its configuration steps are 
applied) to publish key/value pairs that the framework will include in the 
SLF4J {{MDC}} for every log line emitted by the Connector itself and by any 
component (Processor, Controller Service, etc.) running inside the Connector's 
managed flow.

Calls replace the previous set of custom attributes. The framework reserves a 
small set of well-known keys that describe the Connector's own identity 
(identifier, name, type FQN, bundle coordinate); attempts to override these via 
this method are dropped and the framework logs a {{WARN}} for each rejected 
key. The exact set of reserved keys is a framework concern and is intentionally 
not specified in {{{}nifi-api{}}}.

The default throws {{UnsupportedOperationException}} so that frameworks that 
have not yet implemented Connector MDC continue to compile and run; the 
framework-supplied {{StandardConnectorInitializationContext}} overrides it.
h3. {{ProcessGroupStatus.loggingAttributes}}

When a reporting task observes a {{{}ProcessGroupStatus{}}}, it now also sees a 
snapshot of the logging attributes that apply to that Process Group at capture 
time. For PGs inside a connector-managed flow this carries the 
connector-identity keys; for ordinary PGs it carries the existing 
{{processGroup*}} keys. Reporting tasks that export to systems like 
OpenTelemetry can then attach those keys as metric attributes without any 
additional traversal.

Setter takes a defensive immutable copy ({{{}Map.copyOf{}}}); {{null}} is 
normalized to {{{}Map.of(){}}}. {{clone()}} routes through the setter so the 
cloned instance always holds an independent immutable map. {{merge()}} is 
additive (union with last-write-wins per key), which keeps the behavior 
forgiving when cluster nodes are mid-deploy and one node has not yet populated 
its map.
h3. {{ConnectorStatus}}

A connector-level status object analogous to {{{}ProcessGroupStatus{}}}. The 
minimum viable fields are the connector's identity and the status of its 
managed root PG. It is introduced now (rather than reusing 
{{ProcessGroupStatus}} directly from 
{{{}EventAccess.getConnectorStatuses(){}}}) because:
 * Reporting tasks need to identify the connector independently of the root 
PG's id, which may not match the connector id.
 * Connectors have observable state that does not exist at the PG level (e.g. 
connector-wide backlog, idle time, lifecycle state). Future NIPs can add fields 
here without re-shaping the {{EventAccess}} return type.

h3. {{EventAccess.getConnectorStatuses()}}

A new entry point for reporting tasks to enumerate connector-managed flows 
alongside the controller's root flow. Implementations walk the runtime's 
Connector registry, build a {{ConnectorStatus}} for each (populated with the 
existing {{getGroupStatus(...)}} result for the managed root), and return them. 
The default returns an empty list so that:
 * Existing {{EventAccess}} implementations (tests, mocks, downstream 
frameworks) compile unchanged.
 * Existing reporting tasks that do not call this method see no behavior change.

The framework implementation of this method, and the changes required in 
built-in reporting tasks to consume it, are out of scope for this NIP and will 
be tracked under a separate NIFI Jira.
h2. Compatibility

Purely additive:
 * All new interface methods have {{default}} implementations (UOE for 
{{{}setLoggingAttributes{}}}, empty collection for 
{{{}getConnectorStatuses{}}}).
 * The new {{loggingAttributes}} field on {{ProcessGroupStatus}} defaults to 
{{{}Map.of(){}}}; existing serialization round-trips of {{ProcessGroupStatus}} 
are unaffected because the field is not currently present in any persisted 
form. Existing reflective consumers see a new no-arg accessor and an empty 
default.
 * {{ConnectorStatus}} is a brand-new type; nothing references it today.
 * No method signatures, return types, or behaviors are altered.

The Connector API is already marked experimental per NIP-11, but these 
particular additions are designed to be safe to lift into a stable form without 
further changes.
h2. Verification
 * Existing unit tests across {{nifi-api}} continue to compile and pass without 
modification.
 * New unit tests cover:
 ** {{ProcessGroupStatus.setLoggingAttributes}} defensive copy (mutating the 
caller's map after {{set}} does not mutate the stored map), {{clone()}} 
independence, and {{merge()}} union semantics across overlapping/disjoint key 
sets.
 ** {{ConnectorStatus.clone()}} independence (mutating the source 
{{rootGroupStatus}} does not affect the clone).
 ** {{EventAccess.getConnectorStatuses()}} default returns an empty, immutable 
collection.
 * Framework-level integration is verified when the corresponding NiFi 
framework changes consume the new methods (tracked separately).

h2. Alternatives
 # Use {{ProcessGroupStatus}} directly from 
{{EventAccess.getConnectorStatuses()}} (no new type). Rejected because (a) the 
connector's identity is not necessarily the root PG's identity, and (b) 
Connectors are expected to grow connector-level observable state (backlog, idle 
time, lifecycle) that does not fit on {{ProcessGroupStatus}} — introducing the 
wrapper now avoids a return-type breaking change later.
 # Surface custom MDC attributes via a connector-side getter 
({{{}Connector.getLoggingAttributes(){}}}) instead of a push call. Rejected 
because many custom attributes are only known after configuration is applied 
(e.g. database name, source schema), at which point a pull-time getter cannot 
describe them. A push API ({{{}setLoggingAttributes{}}}) handles both 
static-at-init and dynamic-from-config cases uniformly. A getter could be added 
later as a non-breaking convenience for static-only connectors if evidence 
accumulates.
 # Extend {{EventAccess.getControllerStatus()}} to recursively include 
connector-managed flows. Rejected because connector-managed flows are 
intentionally siblings of the root group (per NIP-11); merging them under the 
root would conflate two distinct hierarchies and break existing reporting-task 
assumptions.
 # Promote logging attributes via the existing bulletin/event mechanism rather 
than via a snapshot field on {{{}ProcessGroupStatus{}}}. Rejected because the 
snapshot field is the lowest-friction integration point for the OpenTelemetry 
reporting task and other status-based exporters that already iterate 
{{{}ProcessGroupStatus{}}}.


> Connector Observability: MDC Logging and Reporting-Task Visibility
> ------------------------------------------------------------------
>
>                 Key: NIP-30
>                 URL: https://issues.apache.org/jira/browse/NIP-30
>             Project: NiFi Improvement Proposal
>          Issue Type: Improvement
>            Reporter: Kevin Doran
>            Assignee: Kevin Doran
>            Priority: Major
>
> h2. Motivation
> The Connector extension point introduced by NIP-11 and implemented under 
> NIFI-15258 is a new top-level component whose flow lives inside a managed 
> Process Group that is a sibling of the controller's root Process Group, not a 
> descendant. Two observability gaps follow from that placement:
>  # Logs emitted by components inside a connector-managed flow have no way to 
> carry connector identity in MDC. NiFi already provides MDC context for 
> components in the controller's root flow via the {{processGroup*}} keys, but 
> those keys describe the containing PG hierarchy, not the owning Connector. 
> There is also no mechanism for a runtime operator to publish additional 
> identity-shaped MDC attributes (for example, a connector definition 
> identifier known to the runtime's connector configuration system) for the 
> components running inside a connector-managed flow.
>  # Reporting tasks that walk {{EventAccess.getControllerStatus()}} cannot see 
> connector-managed flows at all. Because connector-managed PGs are siblings of 
> the root, the existing controller traversal misses them entirely; built-in 
> and third-party reporting tasks therefore export zero metrics for connector 
> flows today.
> This NIP proposes a small, additive set of API changes across {{nifi-api}} 
> and {{nifi-framework-api}} to close both gaps without affecting existing 
> components, callers, or test stubs. The framework wiring that supplies 
> behavior behind these defaults is out of scope and will be tracked in a 
> separate NIFI Jira.
> h2. Scope
> Four additions and one field, all backward compatible by virtue of default 
> methods, default field values, and no removed/renamed members:
>  # New default method on 
> {{org.apache.nifi.components.connector.ConnectorConfigurationProvider}} 
> ({{{}nifi-framework-api{}}}):
> {{default Map<String, String> getLoggingAttributes(String connectorId) {    
> return Map.of();
> }}}
>  # New {{loggingAttributes}} field on {{ProcessGroupStatus}} ({{{}Map<String, 
> String>{}}}, defaults to {{{}Map.of(){}}}) with accessors that take a 
> defensive immutable copy on set; included in {{clone()}} and in the static 
> {{merge()}} helper (additive/union merge across cluster nodes).
>  # New status type {{org.apache.nifi.controller.status.ConnectorStatus}} 
> carrying connector identity ({{{}id{}}}, {{{}name{}}}) and the 
> {{ProcessGroupStatus}} for the Connector's managed root Process Group 
> ({{{}rootGroupStatus{}}}). Implements {{Cloneable}} and follows the 
> established Status-class conventions ({{{}PortStatus{}}}, 
> {{{}ConnectionStatus{}}}, etc.). Positioned to grow with connector-level 
> metrics that have no Process-Group analog (for example, connector-wide 
> backlog or idle time) in subsequent work.
>  # New default method on {{{}EventAccess{}}}:
> {{default Collection<ConnectorStatus> getConnectorStatuses() {    return 
> Collections.emptyList();
> }}}
> No existing types, methods, or fields are altered. No new compile-time 
> dependencies are introduced; consumer-side imports are limited to 
> {{org.apache.nifi.controller.status.ConnectorStatus}} ({{{}nifi-api{}}}) and 
> the new method on the existing {{ConnectorConfigurationProvider}} 
> ({{{}nifi-framework-api{}}}).
> h2. Description
> h3. {{ConnectorConfigurationProvider.getLoggingAttributes(String)}}
> {{ConnectorConfigurationProvider}} is a {{nifi-framework-api}} extension 
> point already used by external configuration systems. A provider 
> implementation has the inventory needed to map a connector identifier to 
> identity-shaped attributes that originate outside the connector's own NAR — 
> for example, an externally-issued connector definition identifier that all 
> instances of a given connector type share.
> The framework consults this method once, at connector create time (within 
> {{{}ExtensionBuilder.buildConnector{}}}), and merges the returned map into 
> the MDC context that wraps the connector's {{ComponentLog}} and the loggers 
> of every component (Processor, Controller Service, etc.) running inside the 
> Connector's managed flow. The framework reserves a small set of well-known 
> keys that describe the Connector's own identity ({{{}connectorId{}}}, 
> {{{}connectorName{}}}, {{{}connectorComponent{}}}, 
> {{{}connectorBundleGroup{}}}, {{{}connectorBundleArtifact{}}}, 
> {{{}connectorBundleVersion{}}}); attempts to override these via the returned 
> map are dropped and the framework logs a {{WARN}} for each rejected key. The 
> exact set of reserved keys is a framework concern and is intentionally not 
> specified in {{{}nifi-framework-api{}}}.
> The default returns {{Map.of()}} so that:
>  * Apache NiFi deployments (no provider configured) get the framework-managed 
> identity keys and nothing else.
>  * Existing provider implementations compile unchanged and contribute no 
> additional MDC until they opt in.
> Why a provider method instead of a connector-side push:
>  * The trust boundary stays at the runtime operator, not the NAR author. Only 
> the runtime's chosen {{ConnectorConfigurationProvider}} can publish MDC; 
> arbitrary connector NARs cannot.
>  * The provider already owns the 
> connector-identifier-to-external-configuration mapping that the new attribute 
> values come from; surfacing them is reading an existing source of truth 
> rather than threading a new one through the API.
>  * Mid-lifecycle refresh of these attributes is not currently in scope; a 
> static-at-create model is sufficient for the identifier use case driving this 
> NIP, and a refresh hook can be added later without breaking the default.
> h3. {{ProcessGroupStatus.loggingAttributes}}
> When a reporting task observes a {{{}ProcessGroupStatus{}}}, it now also sees 
> a snapshot of the logging attributes that apply to that Process Group at 
> capture time. For PGs inside a connector-managed flow this carries the 
> connector-identity keys (both framework-managed and provider-supplied); for 
> ordinary PGs it carries the existing {{processGroup*}} keys. Reporting tasks 
> that export to systems like OpenTelemetry can then attach those keys as 
> metric attributes without any additional traversal.
> Setter takes a defensive immutable copy ({{{}Map.copyOf{}}}); null is 
> normalized to {{{}Map.of(){}}}. {{clone()}} routes through the setter so the 
> cloned instance always holds an independent immutable map. {{merge()}} is 
> additive (union with last-write-wins per key), which keeps the behavior 
> forgiving when cluster nodes are mid-deploy and one node has not yet 
> populated its map.
> h3. {{ConnectorStatus}}
> A connector-level status object analogous to {{{}ProcessGroupStatus{}}}. The 
> minimum viable fields are the connector's identity and the status of its 
> managed root PG. It is introduced now (rather than reusing 
> {{ProcessGroupStatus}} directly from 
> {{{}EventAccess.getConnectorStatuses(){}}}) because:
>  * Reporting tasks need to identify the connector independently of the root 
> PG's id, which may not match the connector id.
>  * Connectors have observable state that does not exist at the PG level (e.g. 
> connector-wide backlog, idle time, lifecycle state). Future NIPs can add 
> fields here without re-shaping the {{EventAccess}} return type.
> h3. {{EventAccess.getConnectorStatuses()}}
> A new entry point for reporting tasks to enumerate connector-managed flows 
> alongside the controller's root flow. Implementations walk the runtime's 
> Connector registry, build a {{ConnectorStatus}} for each (populated with the 
> existing {{getGroupStatus(...)}} result for the managed root), and return 
> them. The default returns an empty list so that:
>  * Existing {{EventAccess}} implementations (tests, mocks, downstream 
> frameworks) compile unchanged.
>  * Existing reporting tasks that do not call this method see no behavior 
> change.
> The framework implementation of this method, and the changes required in 
> built-in reporting tasks to consume it, are out of scope for this NIP and 
> will be tracked under a separate NIFI Jira.
> h2. Compatibility
> Purely additive:
>  * All new interface methods have default implementations ({{{}Map.of(){}}} 
> for {{{}getLoggingAttributes{}}}, empty collection for 
> {{{}getConnectorStatuses{}}}).
>  * The new {{loggingAttributes}} field on {{ProcessGroupStatus}} defaults to 
> {{{}Map.of(){}}}; existing serialization round-trips of 
> {{ProcessGroupStatus}} are unaffected because the field is not currently 
> present in any persisted form. Existing reflective consumers see a new no-arg 
> accessor and an empty default.
>  * {{ConnectorStatus}} is a brand-new type; nothing references it today.
>  * No method signatures, return types, or behaviors are altered.
> The Connector API is already marked experimental per NIP-11, but these 
> particular additions are designed to be safe to lift into a stable form 
> without further changes.
> h2. Verification
>  * Existing unit tests across {{nifi-api}} and {{nifi-framework-api}} 
> continue to compile and pass without modification.
>  * New unit tests cover:
>  ** {{ProcessGroupStatus.setLoggingAttributes}} defensive copy (mutating the 
> caller's map after set does not mutate the stored map), {{clone()}} 
> independence, and {{merge()}} union semantics across overlapping/disjoint key 
> sets.
>  ** {{ConnectorStatus.clone()}} independence (mutating the source 
> {{rootGroupStatus}} does not affect the clone).
>  ** {{EventAccess.getConnectorStatuses()}} default returns an empty, 
> immutable collection.
>  ** {{ConnectorConfigurationProvider.getLoggingAttributes(String)}} default 
> returns an empty, immutable map.
>  * Framework-level integration (the framework call into the provider, 
> reserved-key filtering, MDC propagation, and {{EventAccess}} population) is 
> verified when the corresponding NiFi framework changes consume the new 
> methods (tracked separately).
> h2. Alternatives
>  # Use {{ProcessGroupStatus}} directly from 
> {{EventAccess.getConnectorStatuses()}} (no new type). Rejected because (a) 
> the connector's identity is not necessarily the root PG's identity, and (b) 
> Connectors are expected to grow connector-level observable state (backlog, 
> idle time, lifecycle) that does not fit on {{ProcessGroupStatus}} — 
> introducing the wrapper now avoids a return-type breaking change later.
>  # Surface custom MDC attributes via a connector-side push on 
> {{ConnectorInitializationContext}} (e.g. {{setLoggingAttributes(Map)}} 
> invoked from inside {{{}Connector.initialize(...){}}}). Rejected because (a) 
> it places the trust boundary at the NAR author rather than at the framework 
> level, allowing any third-party connector to inject arbitrary MDC keys that 
> downstream observability pipelines would interpret as first-class identity, 
> and (b) the static-at-create model is sufficient for the identifier use case 
> driving this NIP — dynamic-from-config attributes can be added later as a 
> refresh hook on the provider without breaking the API. The provider-side pull 
> does not preclude a connector-side push being added later if a use case 
> emerges.
>  # Extend {{EventAccess.getControllerStatus()}} to recursively include 
> connector-managed flows. Rejected because connector-managed flows are 
> intentionally siblings of the root group (per NIP-11); merging them under the 
> root would conflate two distinct hierarchies and break existing 
> reporting-task assumptions.
>  # Promote logging attributes via the existing bulletin/event mechanism 
> rather than via a snapshot field on {{{}ProcessGroupStatus{}}}. Rejected 
> because the snapshot field is the lowest-friction integration point for the 
> OpenTelemetry reporting task and other status-based exporters that already 
> iterate {{{}ProcessGroupStatus{}}}.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to