Skip to content

notify: add Kafka receiver - #5410

Open
ultinous-cshorvath wants to merge 2 commits into
prometheus:mainfrom
Ultinous:kafka_receiver
Open

notify: add Kafka receiver#5410
ultinous-cshorvath wants to merge 2 commits into
prometheus:mainfrom
Ultinous:kafka_receiver

Conversation

@ultinous-cshorvath

Copy link
Copy Markdown

Pull Request Checklist

Please check all the applicable boxes.

  • Please list all open issue(s) discussed with maintainers related to this change
  • Is this a new Receiver integration?
  • Is this a bugfix?
    • I have added tests that can reproduce the bug which pass with this bugfix applied
  • Is this a new feature?
    • I have added tests that test the new feature's functionality
  • Does this change affect performance?
    • I have provided benchmarks comparison that shows performance is improved or is not degraded
      • You can use benchstat to compare benchmarks
    • I have added new benchmarks if required or requested by maintainers
  • Is this a breaking change?
    • My changes do not break the existing cluster messages
    • My changes do not break the existing api
  • I have added/updated the required documentation
  • I have signed-off my commits
  • I will follow best practices for contributing to this project

Which user-facing changes does this PR introduce?

[FEATURE] notify: Add an Apache Kafka receiver using the webhook v4 JSON message format.

Signed-off-by: cshorvath <cshorvath@ultinous.com>
@ultinous-cshorvath
ultinous-cshorvath requested a review from a team as a code owner July 29, 2026 10:08
@ultinous-cshorvath ultinous-cshorvath changed the title Kafka receiver notify: add Kafka receiver Jul 29, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Added an Apache Kafka notification receiver that publishes webhook v4 JSON messages. Added Kafka configuration, receiver wiring, documentation, metrics registration, and tests. Added integration lifecycle cleanup during receiver construction, reload, and shutdown.

Changes

Kafka receiver

Layer / File(s) Summary
Kafka notifier and configuration
notify/kafka/*, notify/kafka/*_test.go
Adds Kafka configuration validation and a notifier that publishes grouped alerts as webhook v4 JSON records.
Receiver configuration and wiring
config/..., docs/..., notify/metrics.go, CHANGELOG.md, kafka/kafka.go
Adds kafka_configs, constructs Kafka integrations, registers Kafka metrics, and documents the receiver.
Integration resource cleanup
notify/notify.go, notify/integration_close_test.go, app/reloader.go, app/reloader_test.go
Adds integration closing and aggregates cleanup errors during construction, reload, and shutdown.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Alertmanager
  participant KafkaNotifier as notify/kafka.Notifier
  participant KafkaProducer
  participant KafkaBroker
  Alertmanager->>KafkaNotifier: Notify grouped alerts
  KafkaNotifier->>KafkaNotifier: Render webhook v4 JSON
  KafkaNotifier->>KafkaProducer: ProduceSync topic, group key, JSON value
  KafkaProducer->>KafkaBroker: Publish Kafka record
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: adding a Kafka receiver.
Description check ✅ Passed The description completes the applicable checklist items and documents the issue, tests, documentation, API compatibility, and release note.
Linked Issues check ✅ Passed The changes satisfy issue #1996 by adding Kafka notification support, configuration, tests, and documentation.
Out of Scope Changes check ✅ Passed The code, lifecycle cleanup, tests, documentation, and changelog updates support the Kafka receiver objective and are in scope.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (2)
notify/kafka/config.go (1)

45-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Wrap propagated Kafka errors with operation context.

Direct error returns make YAML parsing and producer construction failures harder to locate. Wrap each propagated error with fmt.Errorf("kafka: <operation>: %w", err).

  • notify/kafka/config.go#L45-L53: wrap YAML unmarshal and client-option validation errors.
  • notify/kafka/kafka.go#L46-L55: wrap configuration validation and producer-option construction errors.

As per coding guidelines, “Wrap errors with fmt.Errorf("...: %w", err) and check with errors.Is/errors.As in Go code.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@notify/kafka/config.go` around lines 45 - 53, Wrap the propagated errors in
Config unmarshalling and validate using fmt.Errorf with “kafka: <operation>: %w”
context, covering both sites: notify/kafka/config.go lines 45-53 for YAML
unmarshal and client-option validation, and notify/kafka/kafka.go lines 46-55
for configuration validation and producer-option construction. Preserve error
unwrapping so callers can continue using errors.Is and errors.As.

Source: Coding guidelines

app/reloader.go (1)

127-130: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add receiver context to the joined error.

A receiver build failure is returned without the receiver name. Wrap err before joining it with cleanup errors.

As per coding guidelines, “Wrap errors with fmt.Errorf("...: %w", err) and check with errors.Is/errors.As in Go code.”

Proposed fix
-			return errors.Join(err, notify.CloseIntegrations(integrations))
+			return errors.Join(
+				fmt.Errorf("build receiver %q: %w", rcv.Name, err),
+				notify.CloseIntegrations(integrations),
+			)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/reloader.go` around lines 127 - 130, Update the receiver build error
handling around BuildReceiverIntegrations to wrap err with receiver context
using fmt.Errorf and %w before joining it with
notify.CloseIntegrations(integrations), including the receiver name in the
message while preserving errors.Is/errors.As unwrapping.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/configuration.md`:
- Around line 2039-2042: Update both occurrences of the producer acknowledgement
documentation in docs/configuration.md to remove the claim that acks: "all"
enables idempotent writes. Keep the description focused on waiting for all
in-sync replicas, while preserving the existing acknowledgement-level behavior
and syntax.

---

Nitpick comments:
In `@app/reloader.go`:
- Around line 127-130: Update the receiver build error handling around
BuildReceiverIntegrations to wrap err with receiver context using fmt.Errorf and
%w before joining it with notify.CloseIntegrations(integrations), including the
receiver name in the message while preserving errors.Is/errors.As unwrapping.

In `@notify/kafka/config.go`:
- Around line 45-53: Wrap the propagated errors in Config unmarshalling and
validate using fmt.Errorf with “kafka: <operation>: %w” context, covering both
sites: notify/kafka/config.go lines 45-53 for YAML unmarshal and client-option
validation, and notify/kafka/kafka.go lines 46-55 for configuration validation
and producer-option construction. Preserve error unwrapping so callers can
continue using errors.Is and errors.As.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 00bb60ba-8a61-44d4-9c0e-4ab3746914f7

📥 Commits

Reviewing files that changed from the base of the PR and between 75f3d55 and 9daf1f5.

📒 Files selected for processing (17)
  • CHANGELOG.md
  • app/reloader.go
  • app/reloader_test.go
  • config/config.go
  • config/config_test.go
  • config/receiver/receiver.go
  • config/receiver/receiver_test.go
  • docs/configuration.md
  • docs/integrations.md
  • kafka/kafka.go
  • notify/integration_close_test.go
  • notify/kafka/config.go
  • notify/kafka/config_test.go
  • notify/kafka/kafka.go
  • notify/kafka/kafka_test.go
  • notify/metrics.go
  • notify/notify.go

Comment thread docs/configuration.md
Comment on lines +2039 to +2042
# Producer acknowledgement level. With "none", broker-side delivery failures
# cannot be reported to Alertmanager. "leader" waits for the partition leader.
# "all" waits for all in-sync replicas and enables idempotent writes.
[ acks: <"none" | "leader" | "all"> | default = "leader" ]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Occurrences of acks configuration in docs/configuration.md:"
rg -n 'acks|idempotent|franz-go|kgo' docs/configuration.md || true

echo
echo "Relevant lines around docs/configuration.md:2028-2048:"
sed -n '2028,2050p' docs/configuration.md | nl -ba -v2028

echo
echo "Check git diff for docs/configuration.md and references to producer/acks/idempotent:"
git diff -- docs/configuration.md | sed -n '1,160p' || true

Repository: prometheus/alertmanager

Length of output: 2812


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Configuration file references for franz-go producer settings:"
rg -n 'franz-go|idempotent|acks|enable-idempotent|idempotent' . -g '!vendor/**' -g '!node_modules/**' || true

echo
echo "docs/configuration.md lines 2305-2328:"
sed -n '2305,2328p' docs/configuration.md

Repository: prometheus/alertmanager

Length of output: 15698


🌐 Web query:

twmb franz-go kgo NewClient idempotent producer default produce producer transaction producer doc DefaultProduceTopic idempotent

💡 Result:

The twmb/franz-go Kafka client is designed with an idempotent producer enabled by default [1][2][3]. This aligns with modern Kafka standards where idempotent production is considered a best practice for ensuring exactly-once semantics per partition and avoiding duplicates in the face of retries [1][4]. Key details regarding this behavior include: Idempotent Producer Default: The kgo client automatically opts into idempotent writes [1][3]. This functionality can be explicitly disabled using the DisableIdempotentWrite producer option if required (e.g., due to specific cluster permission constraints like missing IDEMPOTENT_WRITE privileges) [5][3]. When idempotency is disabled, the client defaults to a lower number of in-flight requests per broker compared to the default of 5 used when idempotency is enabled [5]. Transactional Producer: Transactional producing is also fully supported [1][6]. While the producer itself is idempotent by default, transactional operations are treated as an extension of the idempotent producer [1]. You initiate transactions manually via BeginTransaction and EndTransaction calls [1][7]. Note that configuring a transactional ID is incompatible with disabling idempotency; the client will return an error if you attempt to use both [5]. Regarding DefaultProduceTopic: There is no specific "DefaultProduceTopic" setting in the kgo client configuration that automatically assigns a topic to all produce requests. When using the Produce method, you must explicitly specify the topic (typically within the kmsg.ProduceRecord or similar record structure) for each message you intend to send [1][8]. Summary of Key Options: - DisableIdempotentWrite: Used to opt out of idempotent production [5]. - TransactionalID: Setting this enables transactional producer capabilities [5][7]. In practice, users should generally keep the default idempotent settings unless there is a specific architectural reason to disable them, as they provide strong delivery guarantees without performance penalties [1][5][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "kafka/kafka.go factory/body around kopts construction:"
sed -n '95,140p' kafka/kafka.go

echo
echo "Shared kafka config validation construction:"
sed -n '65,135p' notify/kafka/config.go

echo
echo "Docs around line 2038-2043 and 2314-2320:"
python3 - <<'PY'
from pathlib import Path
for path in ['docs/configuration.md']:
    text = Path(path).read_text()
    lines = text.splitlines()
    for start,end in [(2038,2043),(2314,2320)]:
        print(f"\n--- {path}:{start}-{end} ---")
        for i,l in enumerate(lines[start-1:end], start=start):
            print(f"{i}: {l}")
PY

Repository: prometheus/alertmanager

Length of output: 2675


🌐 Web query:

kw "acks=all" "EnableIdempotentWrite" "EnableIdempotentWrites" "DisableIdempotentWrite" franz-go config.go

💡 Result:

In the franz-go (kgo) library, idempotent production is enabled by default [1][2]. The library does not use an "EnableIdempotentWrite" or "EnableIdempotentWrites" option because idempotency is the standard operating mode [3][4]. To opt out of this behavior, you use the DisableIdempotentWrite() producer option [3][5]. This is typically only necessary if you require specific configurations, such as producing with no acknowledgments (acks=none) or only leader acknowledgments (acks=leader), which are incompatible with the default idempotent settings [1][6]. Regarding Kafka acknowledgments and idempotency: - Idempotent production requires acks=all (or equivalent) to function correctly, as it relies on server-side deduplication [3]. - If you use DisableIdempotentWrite(), you can configure other acknowledgment levels like kgo.LeaderAck() or kgo.NoAck() [6]. - When idempotency is disabled, the default maximum number of in-flight produce requests per broker is 1 [3]. If idempotency is enabled, this limit is 5 (for Kafka v1+) or 1 (for Kafka v0.11) [3]. In summary, there is no "Enable" option; simply omit DisableIdempotentWrite() to maintain the default idempotent behavior [1][2].

Citations:


🌐 Web query:

twmb franz-go package config.go acks default producer Acks default ProduceClient defaults producer option

💡 Result:

In the twmb/franz-go library, the default configuration for producer acknowledgments (acks) is AllISRAcks [1]. While the Acks documentation in config.go contains a comment stating "The default is LeaderAck" [1], this appears to be a documentation discrepancy when compared to the library's actual implementation and stated delivery guarantees. The library supports and enforces strong delivery guarantees by default, consistent with KIP-679 [2][3][4]. Specifically, franz-go defaults to idempotent production [5][3], and the library code explicitly initializes the producer configuration with AllISRAcks() [1]. Key details regarding producer acks in franz-go: 1. Default Acks: The client defaults to AllISRAcks() (corresponding to Kafka acks=-1), which ensures all in-sync replicas acknowledge the record before the leader replies [1]. 2. Configuration Options: - RequiredAcks(Acks): This ProducerOpt is used to override the default acknowledgment setting [1]. - Acks Types: - NoAck: Corresponds to acks=0 (no acknowledgment) [1]. - LeaderAck: Corresponds to acks=1 (leader acknowledgment only) [1]. - AllISRAcks: Corresponds to acks=-1 (all in-sync replicas) [1]. 3. Idempotency: franz-go uses idempotent production by default [5]. If idempotency is disabled, certain constraints apply (e.g., specific requirements for inflight requests and acks) [1]. If you need to change the acknowledgment behavior, you can pass the kgo.RequiredAcks option when initializing the kgo.Client [1].

Citations:


Do not treat acks: all as enabling idempotent writes.

franz-go enables idempotent production by default. acks: all only affects the broker acknowledgment level, not the idempotency setting. Remove the idempotency claim from both docs/configuration.md occurrences.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/configuration.md` around lines 2039 - 2042, Update both occurrences of
the producer acknowledgement documentation in docs/configuration.md to remove
the claim that acks: "all" enables idempotent writes. Keep the description
focused on waiting for all in-sync replicas, while preserving the existing
acknowledgement-level behavior and syntax.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for sending notification to kafka

2 participants