Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
56f7bb7
feat: add config example validation script and workflow
patrick-stephens Jul 17, 2026
80a24e3
fix: suppress known failures due to unrelated Fluent Bit issue 12113
patrick-stephens Jul 19, 2026
a503fe3
fix: resolve incorrect syntax in parsers configuration
patrick-stephens Jul 19, 2026
4b9c5ca
fix: missing quote for dummy config in geoip filter
patrick-stephens Jul 19, 2026
527c2d8
fix: incorrect comment format used in buffering examples
patrick-stephens Jul 19, 2026
6d7e5c9
fix: naming of parser files
patrick-stephens Jul 19, 2026
a132634
fix: incorrect format and quoting for dynatrace
patrick-stephens Jul 19, 2026
d99381e
fix: remove redundant and incorrect parser definition for gelf output
patrick-stephens Jul 19, 2026
f4ac340
fix: rename tab titles to match usage for containers
patrick-stephens Jul 19, 2026
2e0e31f
fix: use correct language block for legacy config in cpu-metrics input
patrick-stephens Jul 19, 2026
98406cc
fix: remove tab for conditional processing YAML definition block
patrick-stephens Jul 19, 2026
c22f888
fix: suppress known failures due to Windows kubelet config
patrick-stephens Jul 19, 2026
30f2e45
feat: add multi-example validation
patrick-stephens Jul 19, 2026
9d94865
fix: additional failures in tail examples
patrick-stephens Jul 19, 2026
4758ed7
fix: incorrect elasticsearch output name used in routing
patrick-stephens Jul 19, 2026
b8d553f
fix: resolve routing examples
patrick-stephens Jul 19, 2026
782be6d
fix: resolve loki examples
patrick-stephens Jul 19, 2026
2538636
feat: remove common leading indentation to prevent spurious failures …
patrick-stephens Jul 19, 2026
af69845
feat: support configurable image
patrick-stephens Jul 19, 2026
67b9f8f
fix: resolve sql indentiation issue
patrick-stephens Jul 19, 2026
a303da3
fix: resolve missing AVRO support in release FB for Kafka
patrick-stephens Jul 19, 2026
b30f5f9
fix: resolve s3 examples
patrick-stephens Jul 19, 2026
b4f4f68
fix: resolve lua in kafka examples
patrick-stephens Jul 19, 2026
139ccd3
fix: resolve quoting for blob input
patrick-stephens Jul 19, 2026
381b212
fix: resolve examples for K8S filter
patrick-stephens Jul 19, 2026
45086a5
docs: fix Vale and markdownlint findings across PR files
eschabell Jul 31, 2026
9fda3df
docs: ci: fix example validation workflow and script defects
eschabell Aug 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .github/workflows/pr-example-validation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Validate example configurations in PRs

on:
pull_request:
paths:
- '**/*.md'

permissions: {}

# Avoid multiple commits on the same PR racing to update the comment.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
validate-configs:
name: Validate example configurations in changed Markdown files
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Check out code
uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: false

- name: Get changed Markdown files
# We do not use the usual changed files action because of previous security issues.
id: changed_files
# This complex command finds files changed in the PR, filtering for `.md` and also ensuring they are in the `docs` directory.
# It ensures we only process files that are part of the PR and match our pattern.
# Note: The `github.event.pull_request.base.sha` is used to get the base commit of the PR, which is important for accurate diffing.
#
# On pull requests, HEAD refers to the merge commit.
# We want to compare against the base of the PR.
# Use git diff to get files changed between the PR base and the current commit.
# --name-only lists only the names of the files.
# --diff-filter=AMCR will only show Added, Modified, Copied, Renamed files.
# We grep for *.md files you are interested in.
# The 'docs/' prefix is a common convention but adjust as necessary.
run: |
changed_md=$(git diff --name-only --diff-filter=AMCR ${{ github.event.pull_request.base.sha }}...HEAD | grep '\.md$' || true)

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Workflow snippets:"
if [ -f .github/workflows/pr-example-validation.yaml ]; then
  nl -ba .github/workflows/pr-example-validation.yaml | sed -n '1,140p'
else
  echo "workflow file missing"
fi

echo
echo "Changed diff command occurrences:"
rg -n "changed_md=\\$\\(git diff|\\|\\| true|changed_files" .github/workflows/pr-example-validation.yaml || true

echo
echo "Behavioral sim for grep failure modes:"
bash -c 'set -o pipefail; failed=0; changed_md=$(echo "README.md" | grep -x "not-there" || true); echo "grep_missing_changed_md=(empty)=[$changed_md]"'
bash -c 'set -o pipefail; changed_md=$(rm -f /tmp/nowhere-$$ && cat /tmp/nowhere-$$ 2>/dev/null | grep -x "README.md" || true); echo "grep_diff_failed_changed_md=(empty)=[$changed_md]"; exit_code=$?; echo "pipeline_exit=$exit_code" '
bash -c 'git diff --name-only --diff-filter=AMCR invalid-sha...HEAD; echo "diff_invalid_exit=$?"; true'

Repository: fluent/fluent-bit-docs

Length of output: 219


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Workflow snippets:"
if [ -f .github/workflows/pr-example-validation.yaml ]; then
  awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/pr-example-validation.yaml | sed -n '1,140p'
else
  echo "workflow file missing"
fi

echo
echo "Changed diff command occurrences:"
grep -n "changed_md=\$(git diff\|\\|\\| true\|changed_files" .github/workflows/pr-example-validation.yaml || true

echo
echo "Behavioral sim for grep failure modes:"
bash -c 'set -o pipefail; failed=0; changed_md=$(echo "README.md" | grep -x "not-there" || true); echo "grep_missing_changed_md=(empty)=[$changed_md]"'
bash -c 'set -o pipefail; changed_md=$(rm -f /tmp/nowhere-$$ && cat /tmp/nowhere-$$ 2>/dev/null | grep -x "README.md" || true); echo "grep_diff_failed_changed_md=$(cat)"
exit_code=${PIPESTATUS[@]:?}; echo "exit_code=$exit_code" '
bash -c 'set -o pipefail || true; git diff --name-only --diff-filter=AMCR invalid-sha...HEAD 2>/dev/null || echo "diff_invalid_exit_not_run"; true'

Repository: fluent/fluent-bit-docs

Length of output: 7182


🏁 Script executed:

#!/bin/bash
set -u

echo "Git availability:"
command -v git || true

echo
echo "Diff with invalid SHA and git status:"
(git diff --name-only --diff-filter=AMCR invalid-sha...HEAD 2>&1 || true)
echo "status=$?"

echo
echo "Diff with valid refs when git is available:"
if command -v git >/dev/null 2>&1; then
  git status --short --branch --porcelain
  changed_md=$(git diff --name-only --diff-filter=AMCR HEAD...HEAD 2>&1 || true)
  echo "exit=${PIPESTATUS[0]} changed_md=[$changed_md]"
fi

echo
echo "Bash pipefail/grep behavior:"
bash -c 'set -o pipefail; changed_md=$(printf "README.md\n" | grep -x "not-there" || true); echo "grep_missing_changed_md=[$changed_md]"'
bash -c 'set -e; changed_md=$(rm -f /tmp/nowhere-$$ && cat /tmp/nowhere-$$ 2>/dev/null | grep -x "README.md" || true); echo "should_not_print"; echo "changed_md=[$changed_md]"; false || true'
bash -c 'changed_md=$(rm -f /tmp/nowhere-$$ && cat /tmp/nowhere-$$ 2>/dev/null | grep -x "README.md" || true); echo "changed_md=[$changed_md]"; set -e; false'

Repository: fluent/fluent-bit-docs

Length of output: 626


Fail when git diff cannot compute the changed-file list.

changed_md=$(git diff ... | grep '\.md$' || true) makes the whole command succeed even if git diff encounters an invalid revision or repository error. CHANGED_MD_FILES stays empty, so the validation step prints “No Markdown files changed” and exits successfully.

Compute git diff separately, exit on failure, then filter the output for Markdown files.

🤖 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 @.github/workflows/pr-example-validation.yaml at line 43, Update the
changed-file collection around changed_md so git diff runs separately and its
failure causes the validation step to exit nonzero; only after a successful diff
should its output be filtered for .md files, preserving the existing
no-Markdown-files behavior for an empty result.

echo "Changed Markdown files: $changed_md"
# The value is newline-separated, so it needs the multiline output delimiter syntax.
# A random delimiter avoids any chance of the value terminating the block early.
delimiter=$(openssl rand -hex 16)
{
echo "list<<${delimiter}"
echo "${changed_md}"
echo "${delimiter}"
} >> "$GITHUB_OUTPUT"
shell: bash
Comment thread
eschabell marked this conversation as resolved.

- name: Validate example configurations provided in any changed files
run: |
if [ -z "${CHANGED_MD_FILES:-}" ]; then
echo "No Markdown files changed in this PR. Skipping validation."
exit 0
fi

error_count=0

# Loop through each changed file
for FILE in $CHANGED_MD_FILES; do
echo "Processing changed file: $FILE"
./scripts/test-config.sh "$FILE" || error_count=$((error_count + 1))
done
Comment on lines +65 to +68

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow excerpt =="
sed -n '1,110p' .github/workflows/pr-example-validation.yaml 2>/dev/null || true

echo
echo "== script behavior probe =="
python3 - <<'PY'
import subprocess, shlex

files = ["pipeline/guide with spaces.md", "pipeline/*glob*.md", "pipeline/[brackets].md"]
env = {
    "CHANGED_MD_FILES": "\n".join(files),
    "PATH": "/usr/bin:/bin",
    "SHELL": "/bin/bash",
}
# Simulate the shell loop's explicit arguments by building the command
# as a child process prints argv[0], then exits successfully.
script = r'''
set -u
for FILE in $CHANGED_MD_FILES; do
    printf '%s\n' "$FILE"
done
'''
proc = subprocess.run(["bash", "-c", script], env=env, text=True, capture_output=True)
print("stderr:", proc.stderr.strip() if proc.stderr.strip() else "")
print("stdin:", f"CHANGED_MD_FILES contains {len(files)} filenames")
print("output:")
print(proc.stdout)
PY

Repository: fluent/fluent-bit-docs

Length of output: 3324


Preserve each changed path as one argument.

CHANGED_MD_FILES is newline-separated, but the unquoted for FILE in $CHANGED_MD_FILES splits on whitespace and expands globs. A path containing spaces is invoked as multiple arguments, so pass each line as a single filename. Use a line-by-line read loop instead.

🤖 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 @.github/workflows/pr-example-validation.yaml around lines 65 - 68, Update
the changed-file iteration around CHANGED_MD_FILES to read one line at a time,
preserving each newline-delimited path as a single argument to
./scripts/test-config.sh. Replace the unquoted for loop while retaining the
existing processing message and error_count increment behavior.


if [ $error_count -ne 0 ]; then
echo "ERROR: Validation failed for $error_count file(s)."
exit 1
fi
shell: bash
env:
CHANGED_MD_FILES: ${{ steps.changed_files.outputs.list }}
6 changes: 3 additions & 3 deletions installation/downloads/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ docker run -ti cr.fluentbit.io/fluent/fluent-bit
Use the following command to start Fluent Bit while using a configuration file:

{% tabs %}
{% tab title="fluent-bit.conf" %}
{% tab title="Legacy Configuration: fluent-bit.conf" %}

```shell
docker run -ti -v ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf \
Expand All @@ -24,7 +24,7 @@ docker run -ti -v ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf \

{% endtab %}

{% tab title="fluent-bit.yaml" %}
{% tab title="YAML configuration: fluent-bit.yaml" %}

```shell
docker run -ti -v ./fluent-bit.yaml:/fluent-bit/etc/fluent-bit.yaml \
Expand Down Expand Up @@ -303,7 +303,7 @@ The reasons for using distroless are well covered in
- Reduces false positives on scans (and reduces resources required for scanning).
- Reduces supply chain security requirements to only what you need.
- Helps prevent unauthorised processes or users interacting with the container.
- Less need to harden the container (and container runtime, K8s, and so on).
- Less need to harden the container (and container runtime, Kubernetes, and so on).
- Faster CI/CD processes.

With any choice, there are downsides:
Expand Down
6 changes: 4 additions & 2 deletions pipeline/buffering.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ pipeline:
# Inherits storage.type: filesystem from service

- name: mem
storage.type: memory # Overrides the inherited default
# Overrides the inherited default
storage.type: memory
```

{% endtab %}
Expand All @@ -124,7 +125,8 @@ pipeline:

[INPUT]
Name mem
Storage.Type memory # Overrides the inherited default
# Overrides the inherited default
Storage.Type memory
```

{% endtab %}
Expand Down
2 changes: 1 addition & 1 deletion pipeline/filters/geoip2-filter.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ The following configuration processes the incoming `remote_addr` and appends cou
pipeline:
inputs:
- name: dummy
dummy: {"remote_addr": "8.8.8.8"}
dummy: '{"remote_addr": "8.8.8.8"}'

filters:
- name: geoip2
Expand Down
11 changes: 6 additions & 5 deletions pipeline/filters/kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,11 @@ pipeline:
{% tab title="fluent-bit.conf" %}

```text
[PARSER]
Name custom-tag
Format regex
Regex ^(?<namespace_name>[^_]+)\.(?<pod_name>[a-z0-9](?:[-a-z0-9]*[a-z0-9])?(?:\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)\.(?<container_name>.+)\.(?<container_id>[a-z0-9]{64})
# Ensure this is added to parsers_file loaded in the SERVICE section
# [PARSER]
# Name custom-tag
# Format regex
# Regex ^(?<namespace_name>[^_]+)\.(?<pod_name>[a-z0-9](?:[-a-z0-9]*[a-z0-9])?(?:\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)\.(?<container_name>.+)\.(?<container_id>[a-z0-9]{64})

[INPUT]
Name tail
Expand Down Expand Up @@ -488,7 +489,7 @@ pipeline:
parser: docker
docker_mode: on
mem_buf_limit: 50MB
skip_login_lines: on
skip_long_lines: on
refresh_interval: 10

filters:
Expand Down
6 changes: 3 additions & 3 deletions pipeline/filters/parser.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The plugin supports the following configuration parameters:
| `parser` | Specify the parser name to interpret the field. Multiple parser entries are allowed (one per line). | _none_ |
| `preserve_key` | Keep the original `key_name` field in the parsed result. If false, the field will be removed. | `false` |
| `reserve_data` | Keep all other original fields in the parsed result. If false, all other original fields will be removed. | `false` |
| `Unescape_key` | Deprecated. This option is retained only for backward compatibility and should not be used in new configurations. | _deprecated_ |
| `Unescape_key` | Deprecated. This option is retained only for backward compatibility and shouldn't be used in new configurations. | _deprecated_ |

## Get started

Expand All @@ -27,7 +27,7 @@ The plugin needs a parser file which defines how to parse each field.
This is an example of parsing a record `{"data":"100 0.5 true This is example"}`.

{% tabs %}
{% tab title="fluent-bit.yaml" %}
{% tab title="parser.yaml" %}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

```yaml
parsers:
Expand All @@ -37,7 +37,7 @@ parsers:
```

{% endtab %}
{% tab title="fluent-bit.conf" %}
{% tab title="parser.conf" %}

```text
[PARSER]
Expand Down
4 changes: 2 additions & 2 deletions pipeline/inputs/blob.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ This example excludes certain file patterns and uses filesystem storage for bett
pipeline:
inputs:
- name: blob
path: /data/artifacts/**/*
exclude_pattern: *.tmp,*.bak,*.old
path: '/data/artifacts/**/*'
exclude_pattern: '*.tmp,*.bak,*.old'
storage.type: filesystem
storage.pause_on_chunks_overlimit: true
mem_buf_limit: 50M
Expand Down
2 changes: 1 addition & 1 deletion pipeline/inputs/cpu-metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pipeline:
{% endtab %}
{% tab title="fluent-bit.conf" %}

```shell
```text
[INPUT]
Name cpu
Tag my_cpu
Expand Down
25 changes: 22 additions & 3 deletions pipeline/inputs/kafka.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,16 @@ pipeline:
filters:
- name: lua
match: '*'
script: kafka.lua
call: modify_kafka_message
code: |
local count = 0
function modify_kafka_message(tag, timestamp, record)
count = count + 1
local payload = record.payload
payload.topic = record.topic
payload.status = 'processed by fluent-bit, total records: '..tostring(count)
return 1, timestamp, payload
end

outputs:
- name: kafka
Expand All @@ -118,8 +126,8 @@ pipeline:
[FILTER]
Name lua
Match *
Script kafka.lua
Call modify_kafka_message
Code local count = 0 function modify_kafka_message(tag, timestamp, record) count = count + 1 local payload = record.payload payload.topic = record.topic payload.status = 'processed by fluent-bit, total records: '..tostring(count) return 1, timestamp, payload end

[OUTPUT]
Name kafka
Expand All @@ -134,7 +142,18 @@ The previous example will connect to the broker listening on `kafka-broker:9092`

Since the payload will be in JSON format, the plugin is configured to parse the payload with `format json`.

Every message received is then processed with `kafka.lua` and sent back to the `fb-sink` topic of the same broker.
Every message received is then processed with `kafka.lua` and sent back to the `fb-sink` topic of the same broker, this is shown inline for each configuration but is as follows:

```lua
local count = 0
function modify_kafka_message(tag, timestamp, record)
count = count + 1
local payload = record.payload
payload.topic = record.topic
payload.status = 'processed by fluent-bit, total records: '..tostring(count)
return 1, timestamp, payload
end
```
Comment thread
patrick-stephens marked this conversation as resolved.

The example can be executed locally with `make start` in the `examples/kafka_filter` directory (`docker/compose` is used).

Expand Down
20 changes: 10 additions & 10 deletions pipeline/inputs/tail.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ systemctl edit fluent-bit.service

When the Linux inotify event queue overflows (indicated by an `IN_Q_OVERFLOW` event), Fluent Bit automatically reconciles all monitored files. During reconciliation it:

- Detects rotated files by comparing inodes and file names and re-registers watches as needed.
- Detects rotated files by comparing `inode` numbers and file names and re-registers watches as needed.
- Resets offset tracking for files that have been truncated.
- Retries watch registration for files that previously failed, for example due to `ENOSPC` when `fs.inotify.max_user_watches` is exhausted.

Expand Down Expand Up @@ -272,8 +272,8 @@ pipeline:
path: /var/log/syslog

outputs:
- stdout:
match: *
- name: stdout
match: '*'
```

{% endtab %}
Expand Down Expand Up @@ -368,13 +368,13 @@ pipeline:
{% tab title="fluent-bit.conf" %}

```text
# Note this is generally added to parsers.conf and referenced in [SERVICE]
[PARSER]
Name multiline
Format regex
Regex /(?<time>[A-Za-z]+ \d+ \d+\:\d+\:\d+)(?<message>.*)/
Time_Key time
Time_Format %b %d %H:%M:%S
# Note this is added to parsers.conf and referenced in [SERVICE]
# [PARSER]
# Name multiline
# Format regex
# Regex /(?<time>[A-Za-z]+ \d+ \d+\:\d+\:\d+)(?<message>.*)/
# Time_Key time
# Time_Format %b %d %H:%M:%S

[INPUT]
Name tail
Expand Down
73 changes: 38 additions & 35 deletions pipeline/outputs/dynatrace.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,45 +33,48 @@ To get started with sending logs to Dynatrace:
{% tabs %}
{% tab title="fluent-bit.yaml" %}

```yaml
pipeline:

outputs:
- name: http
match: '*'
header:
- 'Content-Type application/json; charset=utf-8'
- 'Authorization Api-Token {your-API-token-here}'
allow_duplicated_headers: false
host: {your-environment-id}.live.dynatrace.com
port: 443
uri: /api/v2/logs/ingest
format: json
json_date_format: iso8601
json_date_key: timestamp
tls: on
tls.verify: on
```
```yaml
pipeline:
inputs:
- name: dummy
tag: test

outputs:
- name: http
match: '*'
header:
- 'Content-Type application/json; charset=utf-8'
- 'Authorization Api-Token {your-API-token-here}'
allow_duplicated_headers: false
host: '{your-environment-id}.live.dynatrace.com'
port: 443
uri: /api/v2/logs/ingest
format: json
json_date_format: iso8601
json_date_key: timestamp
tls: on
tls.verify: on
```

{% endtab %}
{% tab title="fluent-bit.conf" %}

```text
[OUTPUT]
Name http
Match *
Header Content-Type application/json; charset=utf-8
Header Authorization Api-Token {your-API-token-here}
Allow_Duplicated_Headers false
Host {your-environment-id}.live.dynatrace.com
Port 443
Uri /api/v2/logs/ingest
Format json
Json_Date_Format iso8601
Json_Date_Key timestamp
Tls On
Tls.verify On
```
```text
[OUTPUT]
Name http
Match *
Header Content-Type application/json; charset=utf-8
Header Authorization Api-Token {your-API-token-here}
Allow_Duplicated_Headers false
Host {your-environment-id}.live.dynatrace.com
Port 443
Uri /api/v2/logs/ingest
Format json
Json_Date_Format iso8601
Json_Date_Key timestamp
Tls On
Tls.verify On
```

{% endtab %}
{% endtabs %}
Expand Down
Loading