Skip to content

Add Kafka topic template routing for Kubernetes pod log streams#539

Open
jeremyudis wants to merge 1 commit into
pinterest:masterfrom
jeremyudis:k8s-topic-template-routing
Open

Add Kafka topic template routing for Kubernetes pod log streams#539
jeremyudis wants to merge 1 commit into
pinterest:masterfrom
jeremyudis:k8s-topic-template-routing

Conversation

@jeremyudis

Copy link
Copy Markdown

Summary

Adds writer.kafka.topicTemplate, letting a single log config running in Kubernetes mode (DaemonSet tailing /var/log/pods) route each pod log stream to a Kafka topic derived from its source.

Supported variables:

  • %{namespace} — the pod's Kubernetes namespace (parsed from the namespace_podname_uid pod directory name)
  • %{container} — the container directory in the standard kubelet layout /var/log/pods/<ns>_<pod>_<uid>/<container>/0.log (use logDir=/* so each container directory becomes its own stream)
  • %{metadata:<key>} — a pod label/annotation fetched via the existing podMetadataFields mechanism, enabling self-serve routing (teams annotate their pods with a topic name)

Topic resolution precedence (per stream, at writer creation)

  1. topicTemplate — used only when every variable resolves and the result is a legal Kafka topic name
  2. topic — the required static fallback; legacy \N capture-group expansion against logStreamRegex is unchanged. Fallbacks emit singer.writer.topic_template_fallback

Design notes

  • Pod-level identifiers (%{podName}, %{podUid}) are deliberately rejected at config load time: pods churn on every restart/reschedule, so they would create unbounded topic cardinality.
  • Validation is fail-fast: unsupported variables, malformed %{...} syntax, and illegal topic characters are all ConfigurationExceptions at startup.
  • One Kafka producer per cluster is already shared (KafkaProducerManager), so many resolved topics add no producer overhead.
  • Also fixes RecursiveFSEventProcessor to collect configs from all matching logConfigMap entries instead of stopping at the first, so a wildcard logDir config (e.g. /*) no longer permanently shadows an exact-directory config at the same depth.

Docs

  • docs/configuration_samples/sample_kubernetes/README.md — Kubernetes mode, container stdout tailing, routing precedence, DaemonSet deployment notes
  • docs/configuration_samples/sample_kubernetes/conf.d/container.stdout_logs.properties — working per-container routing example

Test plan

  • TestTopicTemplateResolver (17 tests): template validation (rejects pod-level/unknown variables, malformed syntax, illegal characters), namespace/container/metadata resolution, all unresolvable-variable and illegal-topic fallback paths
  • TestTopicTemplateRouting (5 tests): writer-factory precedence — template wins when resolvable, falls back to static topic otherwise, legacy capture-group expansion preserved
  • TestContainerLogStreams (3 tests): end-to-end with the kubelet fixture — logDir=/* produces one stream per container directory with concrete paths resolving to logs_default_web / logs_default_sidecar; wildcard and exact configs no longer shadow each other
  • TestLogConfigUtils (+4 tests): config parsing accepts valid templates and rejects invalid ones at load time
  • Full regression over the touched areas: TestPodLogCycle, TestPodAllowlist, TestKubeService, TestRecursiveEventProcessor — 71/71 passing

🤖 Generated with Claude Code

Adds writer.kafka.topicTemplate so a single log config running in
Kubernetes mode can route each pod log stream to a topic derived from
its source. Supported variables: %{namespace}, %{container} (the
container directory in the kubelet /var/log/pods layout), and
%{metadata:<key>} (pod labels/annotations via podMetadataFields).

Pod-level identifiers (pod name / uid) are rejected at config load time
to avoid unbounded topic cardinality. Resolution is all-or-nothing: if
any variable is unresolvable or the result is not a legal Kafka topic
name, the writer falls back to the static topic (legacy \N capture
group expansion preserved) and emits a fallback metric.

Also fixes RecursiveFSEventProcessor to collect configs from all
matching logConfigMap entries instead of stopping at the first, so a
wildcard logDir config (e.g. "/*" for per-container directories) no
longer shadows an exact-directory config at the same depth.

Includes a Kubernetes sample config and README documenting container
stdout tailing and routing precedence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jeremyudis jeremyudis requested a review from a team as a code owner June 10, 2026 06:05
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.

1 participant