{"owner":"elastic","repo":"logstash","hasSkills":true,"totalSkillsCount":11,"totalTokensCount":11040,"categories":["root-instruction","claude-rule","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","CLAUDE.md","qa/AGENTS.md","qa/CLAUDE.md","tools/benchmark-cli/README.md","tools/dependencies-report/README.md","tools/jvm-options-parser/README.md","tools/logstash-docgen/README.md","tools/paquet/README.md","x-pack/AGENTS.md","x-pack/CLAUDE.md"],"skills":{"AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to coding agents when working with code in this repository.\n\n## Project Overview\n\nLogstash is a server-side data processing pipeline that ingests data from multiple sources, transforms it, and sends it to various destinations. It's part of the Elastic Stack and is a **hybrid Java/Ruby application** using JRuby.\n\n### JRuby and versions\n\n- The JRuby version is defined in **`versions.yml`** (repo root). The project uses **vendored JRuby only** — no external Ruby, rvm, or rbenv is required; Gradle installs JRuby into `vendor/jruby` via the bootstrap task.\n- The gem path includes a Ruby compatibility version segment (e.g. `vendor/bundle/jruby/X.Y.0`). It is intentionally hardcoded in Gradle (`rubyUtils.gradle`), shell scripts (`bin/logstash.lib.sh`), and Java test helpers (e.g. `RubyEnvTestCase`). To find the current value, grep for `vendor/bundle/jruby/` in `bin/logstash.lib.sh`. Do not treat it as a bug or \"dynamicize\" it when working on the codebase.\n\n### Single source of truth for versions\n\n- **`versions.yml`** in the repository root is the single source of truth for Logstash version, JRuby version, bundled JDK, Jackson, and related coordinates. Gradle loads it in `settings.gradle`; the `logstash-core` and `logstash-core-plugin-api` gemspecs copy from it. When in doubt about a version, check or update `versions.yml`.\n\n## Build Commands\n\nRun all build and test commands from the **repository root** (where `gradlew` and `versions.yml` live), unless a specific task or doc says otherwise.\n\nAll commands require a JDK that meets the `sourceCompatibility` defined in `build.gradle` (currently 17+). JDK 21 is preferred and is the version bundled for distribution (see `bundled_jdk` in `versions.yml`). Ruby tests run via vendored JRuby — no external Ruby, rvm, or rbenv needed.\n\nUse Gradle tasks as the normal entry points from the repo root. Rake and bundle are used as leaf tools where the existing Gradle tasks or scripts already wire them up.\n\n```bash\n# Initial setup (required once)\n./gradlew bootstrap\n\n# Install development dependencies\n./gradlew installDevelopmentGems\n\n# Install default plugins (80+ plugins)\n./gradlew installDefaultGems\n\n# Build distribution artifact (tar.gz)\n./gradlew assembleTarDistribution\n# Other distribution formats (zip, deb, rpm) are built via rake tasks in rakelib/artifacts.rake\n```\n\n**Build performance (CI):** For faster `installDefaultGems`, use `JARS_SKIP=true`; required jars are already vendored or in JRuby stdlib.\n\n## Running Tests\n\n**Test layout:** Unit specs live under **`spec/`** (root) and **`logstash-core/spec/`**. Integration and acceptance tests live under **`qa/`** (e.g. **`qa/integration`** for integration tests). Use the commands below from the repo root; the single-integration-test example uses a path under `qa/integration`.\n\n**Test task structure:** There are two separate Gradle test tasks: `javaTests` (pure Java unit tests) and `rubyTests` (Ruby specs **and** Java tests that depend on Ruby infrastructure). The top-level `test` task excludes all classes itself and simply delegates to both, so `./gradlew test --tests ...` will silently match nothing. Always apply `--tests` to `javaTests` or `rubyTests` directly.\n\n**`javaTests` vs `rubyTests` split:** Several Java test classes are excluded from `javaTests` and included in `rubyTests` because they load Ruby code at runtime. The exclude/include lists are in `logstash-core/build.gradle`. If `--tests` returns \"No tests found for given includes\", the class is likely in the other task. Examples of Java tests that live in `rubyTests`:\n- `OutputDelegatorTest`, `JavaCodecDelegatorTest` — test delegators that wrap Ruby plugin instances\n- `ConfigCompilerTest`, `CompiledPipelineTest`, `EventConditionTest` — test config compilation which invokes the Ruby parser\n- `PluginFactoryExtTest` — tests Ruby plugin instantiation\n\n```bash\n# All unit tests (Java + Ruby)\n./gradlew test\n\n# All Java tests (pure Java, no Ruby dependency)\n./gradlew :logstash-core:javaTests\n\n# Single Java test class\n./gradlew :logstash-core:javaTests --tests org.logstash.TimestampTest\n\n# Single Java test method\n./gradlew :logstash-core:javaTests --tests org.logstash.TimestampTest.testEpochMillis\n\n# Java tests by pattern\n./gradlew :logstash-core:javaTests --tests \"org.logstash.settings.*\"\n\n# WRONG — will silently run nothing because `test` excludes all classes:\n# ./gradlew :logstash-core:test --tests org.logstash.TimestampTest\n\n# Java test that depends on Ruby — must use rubyTests, not javaTests:\n./gradlew :logstash-core:rubyTests --tests org.logstash.config.ir.compiler.OutputDelegatorTest\n\n# All Ruby tests (core) — also runs the Ruby-dependent Java tests listed above\n./gradlew :logstash-core:rubyTests\n\n# Single Ruby spec file\nSPEC_OPTS=\"-fd -P logstash-core/spec/logstash/agent_spec.rb\" \\\n  ./gradlew :logstash-core:rubyTests --tests org.logstash.RSpecTests\n\n# All integration tests\n./gradlew runIntegrationTests\n\n# Single integration test\n./gradlew runIntegrationTests -PrubyIntegrationSpecs=specs/slowlog_spec.rb\n\n# All X-Pack Ruby tests\n./gradlew :logstash-xpack:rubyTests\n\n# Single X-Pack Ruby test\nSPEC_OPTS=\"-fd -P spec/config_management/elasticsearch_source_spec.rb\" \\\n  ./gradlew :logstash-xpack:rubyTests --rerun-tasks\n\n# Single Ruby test for core or X-Pack (fast, direct JRuby)\nbin/rspec logstash-core/spec/logstash/some_spec.rb\nbin/rspec x-pack/spec/config_management/elasticsearch_source_spec.rb\n```\n\n## Quick Verification\n\n```bash\n# Start Logstash with inline config\nbin/logstash -e 'input { stdin { } } output { stdout {} }'\n```\n\n## Architecture\n\n### Dual Java/Ruby Runtime\n- **Java Core** (`logstash-core/src/main/java/org/logstash/`): Pipeline execution engine, event processing, persistent queue, configuration compilation\n- **Ruby Core** (`logstash-core/lib/logstash/`): Agent lifecycle, plugin coordination, REST API, configuration loading, metric store, settings management\n\n### Key Components\n- **Agent** (`logstash-core/lib/logstash/agent.rb`): Main orchestrator, manages pipeline lifecycle\n- **JavaPipeline** (`logstash-core/lib/logstash/java_pipeline.rb`): Ruby class for core event processing with worker threads, backed by Java execution infrastructure in `logstash-core/src/main/java/org/logstash/execution/`\n- **Plugin System** (`logstash-core/lib/logstash/plugins/`): Input/Filter/Output/Codec plugin infrastructure\n- **Plugin API** (`logstash-core-plugin-api/`): Public interface for plugin developers\n- **JvmOptionsParser** (`tools/jvm-options-parser/`): Standalone Java 11 subproject that parses `config/jvm.options` and composes JVM command-line flags before JRuby starts. Supports version-conditional syntax (e.g. `11-:`, `16-20:`), resolves `${ENV_VAR}` references, and enforces mandatory JVM flags. Invoked from `bin/logstash.lib.sh` during `setup_java()`.\n\n### Directory Structure\n- `logstash-core/` - Core framework (Java + Ruby)\n- `lib/pluginmanager/` - Plugin installation and management\n- `x-pack/` - Elastic-licensed features (disable with `OSS=true`)\n- `qa/` - Quality assurance: acceptance, integration, Docker tests\n- `tools/` - Development tools (docgen, benchmarks, release automation)\n\n### Gradle–Ruby interaction\n\n- **`rubyUtils.gradle`** drives Ruby from Gradle: it uses JRuby's `ScriptingContainer`, `executeJruby`, and invokes bundle/rake. The vendored JRuby lives under **`vendor/jruby`** after `./gradlew bootstrap`. When debugging Gradle tasks that run Ruby (e.g. plugin install, Rake), this is where the wiring lives.\n\n## Code Design Practices\n\nWhen modifying code, don't just append to what's there — evaluate whether the surrounding code still makes sense with the change. If a method, class, or abstraction was designed for one case and you're adding a second, consider restructuring so the result reads as if both cases were planned from the start. Each distinct concept should have its own clearly-named unit, and higher-level code should compose those units so the intent is obvious without reading the implementation.\n\n### Plugin System Conventions\n\n- **Inherit from the type-specific base class.** Each plugin type has a base in `logstash-core/lib/logstash/{type}s/base.rb` (inputs, filters, outputs, codecs). Override `register` for setup plus the type-specific entry point: `run` (inputs), `filter` (filters), `receive`/`multi_receive` (outputs), `decode`/`encode` (codecs).\n- **Declare configuration with the `config` DSL**, not raw `@params` access. Use `config :name, :validate => :type, :default => value` — defined in `logstash-core/lib/logstash/config/mixin.rb`. The framework handles validation, coercion, and documentation generation from these declarations.\n- **Set output concurrency at the class level** with `concurrency :shared` or `concurrency :single` in `logstash-core/lib/logstash/outputs/base.rb`. This determines which `OutputStrategy` the pipeline uses to wrap the plugin: `:shared` means multiple worker threads can call the plugin simultaneously; `:single` serialises all calls so only one thread executes the plugin at a time. Most new outputs should use `:shared`.\n- **Plugin lookup goes through `LogStash::Plugins::Registry#lookup`** (`logstash-core/lib/logstash/plugins/registry.rb`). New built-in plugins register in `logstash-core/lib/logstash/plugins/builtin.rb`. External plugins are discovered as gems.\n\n### Delegator and Metrics Patterns\n\n- **Every plugin runs behind a delegator** that adds metrics collection and thread-safety. Filters: `FilterDelegatorExt` (Java), outputs: `OutputDelegatorExt` (Java), codecs: `Codecs::Delegator` (`logstash-core/lib/logstash/codecs/delegator.rb`). When changing plugin behavior, determine whether the change belongs in the delegator (cross-cutting concern) or in the plugin itself.\n- **Metrics follow a namespace hierarchy.** Use `metric.namespace(:sub)` to create children. `NullMetric` implements the same interface but discards data — it's the null-object stand-in when `enable_metric => false` or during testing. Never instantiate metric objects directly; accept them from the framework.\n- **Standard mixins are included automatically.** All plugins get `LogStash::Util::Loggable` (provides `logger`, `slow_logger`, `deprecation_logger`) and `LogStash::Config::Mixin` (config DSL). Additional mixins like `ECSCompatibilitySupport` and `EventFactorySupport` are opt-in per plugin.\n\n### Pipeline Execution Model\n\n- **The worker loop is the hot path.** `WorkerLoop` (`logstash-core/src/main/java/org/logstash/execution/WorkerLoop.java`) pulls batches from the queue, runs them through the compiled pipeline, and signals completion. Shutdown is coordinated via `AtomicBoolean` flags — do not add blocking calls or heavy allocations inside this loop.\n- **Pipeline lifecycle follows a strict ordering** in `JavaPipeline` (`logstash-core/lib/logstash/java_pipeline.rb`): outputs and filters register first, then workers start, then inputs start last. On shutdown the reverse applies: inputs stop first, workers drain, then filters and outputs close. Violating this ordering causes data loss or deadlocks.\n- **Use `Concurrent::AtomicBoolean` (Ruby) or `java.util.concurrent.atomic.AtomicBoolean` (Java) for cross-thread signaling.** Do not use plain boolean instance variables for state shared across threads — Ruby's GIL does not guarantee visibility, and JRuby runs without one.\n\n#### Runtime Code Generation (Config → Java Classes)\n\nLogstash configs are compiled into executable Java classes at runtime through a multi-stage pipeline:\n\n1. **Parse:** Treetop grammar (`logstash-core/lib/logstash/compiler/lscl/lscl_grammar.treetop`) parses `.conf` files into an AST.\n2. **IR:** `ConfigCompiler` (`logstash-core/src/main/java/org/logstash/config/ir/ConfigCompiler.java`) converts the AST into a `PipelineIR` — a directed acyclic graph of plugins and conditionals.\n3. **Compile:** `DatasetCompiler` (`logstash-core/src/main/java/org/logstash/config/ir/compiler/DatasetCompiler.java`) generates Java source strings for each pipeline vertex. `ComputeStepSyntaxElement` (`logstash-core/src/main/java/org/logstash/config/ir/compiler/ComputeStepSyntaxElement.java`) compiles these sources into classes via Janino at runtime. Generated classes live under the `org.logstash.generated` package.\n4. **Execute:** `CompiledPipeline` (`logstash-core/src/main/java/org/logstash/execution/CompiledPipeline.java`) wires the compiled datasets into the worker loop.\n\nConditionals from config (`if [field] == \"value\"`) compile into `EventCondition` lambdas wired into `SplitDataset` nodes. To inspect generated source code, add these flags to `config/jvm.options`:\n```\n-Dorg.codehaus.janino.source_debugging.enable=true\n-Dorg.codehaus.janino.source_debugging.dir=/path/to/output\n```\n\n### Java Code Conventions\n\n- **Packages are organized by functional domain**, not architecture layers. Main packages under `org.logstash`: `execution` (pipeline/workers), `config.ir` (config compilation), `plugins` (plugin infrastructure), `ackedqueue` (persistent queue), `instrument.metrics` (metrics), `ext` (JRuby extensions), `settings` (configuration types), `log` (logging). Place new classes in the package matching their domain.\n- **Naming conventions:** JRuby extension classes use the `Ext` suffix (`LoggableExt`, `AbstractSimpleMetricExt`). Delegator classes use `Delegator` suffix. Abstract bases use the `Abstract` prefix. Most concrete classes are `public final class` — use plain `public class` only when subclassing is intended (e.g. `BaseSetting<T>`).\n- **Prefer unchecked exceptions.** Custom exceptions extend `RuntimeException` (e.g. `FieldReference.IllegalSyntaxException`, `Event.InvalidTagsTypeException`). Checked exceptions appear only when unavoidable (`IOException`, `InterruptedException`). Include descriptive messages and preserve exception chains via the `cause` parameter.\n- **Null handling:** Use `Objects.requireNonNull()` for constructor parameter validation. Use `@Nullable` annotation when null is an intentional part of the contract. Use `Optional` for parse/lookup chains that may fail (see `Timestamp.tryParse`), not as a general null replacement. Prefer explicit null checks before dereference over implicit assumptions.\n- **Logging uses Log4j 2.** Declare loggers as `private static final Logger LOGGER = LogManager.getLogger(MyClass.class)`. Use `TRACE` for diagnostic detail (parse attempts, fallback paths), `WARN` for recoverable issues, `ERROR` for uncaught exceptions, `FATAL` for unrecoverable system errors. Include context (thread name, field type, value) in messages.\n- **Thread safety:** Prefer immutable `final` fields as the primary safety mechanism. Use `ConcurrentHashMap` for thread-safe caches (see `FieldReference.CACHE`). Use `AtomicBoolean` for shutdown signaling. Document thread-safety guarantees in class-level JavaDoc when the class is shared across threads.\n\n### REST API Conventions\n\n- **API modules inherit from `LogStash::Api::Modules::Base`** (`logstash-core/lib/logstash/api/modules/base.rb`), which extends `Sinatra::Base`. Each module receives the `agent` reference and a `CommandFactory` in its constructor. Define routes using Sinatra's `get`/`put` DSL inside the module class.\n- **Routing uses rack namespace mapping.** Endpoints are mounted at path prefixes (`/_node`, `/_stats`, `/_node/stats`, `/_node/plugins`, `/_node/logging`, `/_health_report`) defined in `rack_namespaces` in `logstash-core/lib/logstash/api/rack_app.rb`. Add new endpoint namespaces there, not via ad-hoc Sinatra routing.\n- **All responses go through `respond_with`** (`logstash-core/lib/logstash/api/app_helpers.rb`). It automatically wraps JSON responses with default metadata (host, version, http_address, id) unless `exclude_default_metadata` is passed. Use `respond_with(data)` — do not write raw JSON responses.\n- **Errors use `ApiError` subclasses** (`logstash-core/lib/logstash/api/errors.rb`): `NotFoundError` (404), `BadRequest` (400), `RequestTimeout` (408). Raise these from route handlers; the base module catches them and formats a consistent error envelope with `path`, `status`, and `error` fields.\n- **Business logic lives in command classes**, not in route handlers. Commands inherit from `LogStash::Api::Commands::Base` (`logstash-core/lib/logstash/api/commands/base.rb`) and access metrics via `service.extract_metrics(path, *keys)`. Route handlers should only call `factory.build(:command_name)` and pass results to `respond_with`.\n\n### Java/Ruby Boundary\n\n- **JRuby extensions use `@JRubyClass`/`@JRubyMethod` annotations.** Classes register in `RubyUtil` (`logstash-core/src/main/java/org/logstash/RubyUtil.java`) via `setupLogstashClass` or `defineClassUnder`. Follow existing patterns in `RubyUtil`'s static initializer when adding new Java-backed Ruby classes.\n- **`ThreadContext` must be obtained at point of use** via `RubyUtil.RUBY.getCurrentContext()`, never stored at construction time. Storing it causes `NullPointerException` when used from a different thread (JRuby 10+). See `RubyCodecDelegator` for the correct pattern.\n- **Test the round-trip through Java.** Integration tests should exercise the full path from Ruby through the Java layer and back, not just the Ruby classes in isolation. This catches type coercion issues and metric namespace registration bugs that unit tests miss.\n- **Java tests that load Ruby:** Some Java tests (e.g. `RubyEnvTestCase` and subclasses) set **GEM_HOME** / **GEM_PATH** to the vendored bundle path (`vendor/bundle/jruby/<ruby-compat-version>`) so they can load Logstash Ruby code. Check `bin/logstash.lib.sh` or `rubyUtils.gradle` for the current version segment. That path is intentional; do not change it when touching those tests.\n\n### Test Practices\n\n- **Use `sample_one` helper for filter pipeline tests.** `PipelineHelpers#sample_one` (`logstash-core/spec/support/pipeline/pipeline_helpers.rb`) spins up a real pipeline, feeds events through a filter config, and captures results — preferred over manually constructing pipeline objects.\n- **Create dummy plugin classes inside spec files for isolation.** Define throwaway classes inheriting from the appropriate base, register with `LogStash::PLUGIN_REGISTRY.add`. See `pipeline_helpers.rb` for examples.\n- **Use `shared_examples` for interface contracts and `shared_context` for common setup.** Examples: `\"metrics commons operations\"` in `logstash-core/spec/support/shared_examples.rb`, `\"execution_context\"` in `logstash-core/spec/support/shared_contexts.rb`.\n- **Integration tests use the Fixture + Service pattern.** `Fixture` (`qa/integration/framework/fixture.rb`) bootstraps services and loads config from YAML fixture files under `qa/integration/fixtures/`.\n- **Test that existing rescue blocks catch new code paths.** When a method has a `rescue => e` that returns `nil`, verify that exceptions raised in newly added branches are caught by it. It is easy to accidentally add code outside the `begin...rescue` scope.\n- **Clean up JVM state in `after` blocks.** Any `java.lang.System.setProperty` call in a test must have a corresponding `clearProperty` in `after` to avoid leaking state to other specs.\n- **Prefer concrete test doubles over mocks.** The codebase uses purpose-built classes (`DummyInput`, `DummyFilter`, `DummyOutput` in `logstash-core/spec/support/mocks_classes.rb`) that inherit from real base classes with minimal implementations. Use these over RSpec `double()` when testing pipeline behavior — they exercise real plugin lifecycle methods.\n- **Reserve mocks for infrastructure boundaries.** `LogStash::Agent`, loggers, and `LogStash::SETTINGS` are commonly stubbed to prevent real pipeline startup. Use `allow(obj).to receive(:method).and_call_original` when you need the mock for setup but want to test real behavior. The codebase uses plain `double()` and `.as_null_object` — not `instance_double` or `class_double` — due to JRuby's dynamic method dispatch.\n\n### Test Validity\n\n- **Verify every new test fails without the corresponding change.** Before treating a test as done, confirm it fails against the pre-change code. A test that passes with or without the fix proves nothing. This is the most basic check that a test is load-bearing.\n- **Assert on the specific behaviour the change introduces, not on pre-existing conditions.** If the system already returned `200 OK` before your fix, asserting `200 OK` does not validate the fix. Pin the assertion to an observable difference — a new field, a changed value, a previously-missing log line.\n- **For bug fixes, reproduce the bug in the test first.** Write the test, watch it fail for the same reason the bug manifests, then apply the fix and watch it pass. If you cannot make the test fail, either the bug is not what you think or the test is not targeting it.\n- **Beware of tautological mocks.** Stubbing a method to return the value you then assert is a circular proof. Mocks should simulate collaborators, not replace the code under test. If removing the implementation and keeping only the mock still passes the test, the test is testing the mock.\n\n## Plugin Development\n\nPlugins live in separate repositories under [logstash-plugins](https://github.com/logstash-plugins). Each plugin is a self-contained Ruby gem. Plugin types: input, filter, output, codec.\n\n- The **`logstash-core-plugin-api`** version is in **`versions.yml`**; plugins (in separate repos) depend on it.\n- Templates for new plugins are in **`lib/pluginmanager/templates/`**.\n- Supported plugin versions and compatibility are tracked in the [logstash-plugins `.ci`](https://github.com/logstash-plugins/.ci) repo (e.g. `logstash-versions.yml`).\n\n## Sensitive Data Handling\n\nWhen adding configuration parameters for sensitive data:\n- In Ruby: Apply `:password` validation or wrap with `Logstash::Util::Password`\n- In Java: See `Password.java` and `SecretVariable.java` for patterns\n- Override `toString()` to mask sensitive values\n- Never log secrets at any log level by default\n\nExample:\n```ruby\n:my_auth => { :validate => :password }\n# Or wrap manually:\n::LogStash::Util::Password.new(my_auth)\n```\n\n## Environment Variables\n\n```bash\nexport LOGSTASH_SOURCE=1\nexport LOGSTASH_PATH=/path/to/logstash\nexport JAVA_HOME=/path/to/jdk  # JDK 17+ (see build.gradle sourceCompatibility)\nexport OSS=true                 # Build without X-Pack\n```\n\n## Debugging\n\n```bash\n# Attach debugger to running Logstash\nLS_JAVA_OPTS=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005\" bin/logstash ...\n```\n\n## Breaking Changes Policy\n\n1. Implement new behavior as opt-in (MINOR)\n2. Add deprecation warnings (MINOR)\n3. Change default, keep opt-out option (MAJOR)\n4. Remove old behavior (MAJOR)\n\n## Documentation\n\n### Directory Structure\n- `docs/reference/` - Main user-facing documentation (Markdown)\n- `docs/extend/` - Plugin development and contribution guides (Markdown)\n- `docs/release-notes/` - Version-specific release notes, breaking changes, deprecations\n- `docs/static/` - Static assets (images, OpenAPI specs)\n- `tools/logstash-docgen/` - Auto-generates plugin documentation from code\n\n### Formats\n- **Reference docs**: Markdown (`.md`) with front matter and admonition syntax\n- **Plugin docs**: AsciiDoc (`.asciidoc`) with dynamic ID variables\n\n### Markdown Conventions (Reference Docs)\n```markdown\n---\nmapped_pages:\n  - https://www.elastic.co/guide/en/logstash/current/page.html\n---\n\n# Heading Title [anchor-id]\n\n::::{admonition} Note title\n:class: note\nContent here\n::::\n```\n\n### AsciiDoc Conventions (Plugin Docs)\n```asciidoc\n:plugin: example\n:type: filter\n\n[id=\"plugins-{type}s-{plugin}\"]\n=== Example filter plugin\n\n[id=\"plugins-{type}s-{plugin}-options\"]\n==== Configuration Options\n```\n\nUse dynamic IDs (`{type}`, `{plugin}`) to prevent ID duplication across plugin versions.\n\n### Guidelines\n- All PRs must include documentation updates when applicable\n- Plugin documentation is required before merging\n- Follow Elastic documentation standards: one sentence per line in AsciiDoc, use `[id=\"...\"]` anchors (not Markdown-style), prefer `[source,lang]` for code blocks, use `<<anchor>>` for cross-references, and keep titles in title case\n- Images go in `docs/reference/images/` (PNG format)\n\n## See also\n\n- **CONTRIBUTING.md** — Contribution process, sensitive data, and where to ask questions\n- **STYLE.md** — Ruby style (indentation, logging, hash syntax) and consistency\n","CLAUDE.md":"See `AGENTS.md`.\n","qa/AGENTS.md":"# qa/AGENTS.md\n\nGuidance for coding agents working with the QA test infrastructure. See also the root `AGENTS.md` for general project conventions.\n\n## Overview\n\nThe `qa/` directory contains three independent test suites:\n\n| Suite | Path | Purpose |\n|-------|------|---------|\n| **Integration** | `qa/integration/` | End-to-end tests with live services (Elasticsearch, Kafka, etc.) |\n| **Acceptance** | `qa/acceptance/` | Package artifact testing (deb, rpm install/upgrade) |\n| **Docker** | `qa/docker/` | Docker image and container verification |\n\nEach suite has its own dependencies and execution workflow.\n\n## Integration Tests (qa/integration/)\n\n### Running\n\n```bash\n# All integration tests via Gradle (handles all setup automatically)\n./gradlew runIntegrationTests\n\n# Single test\n./gradlew runIntegrationTests -PrubyIntegrationSpecs=specs/dlq_spec.rb\n\n# Manual: set up artifacts first, then run directly from qa/integration/\n./gradlew installIntegrationTestGems  # build Logstash tarball + install test gems\n./gradlew copyEs                      # download Elasticsearch (if spec needs it)\n./gradlew copyFilebeat                # download Filebeat (if spec needs it)\ncd qa/integration\nBUNDLE_PATH=../../build/qa/integration/vendor \\\n  ../../vendor/jruby/bin/jruby -S bundle exec rspec specs/dlq_spec.rb\n```\n\nWhich artifact tasks to run depends on the `services:` list in each spec's fixture YAML.\n### Architecture: Fixture + Service Pattern\n\nEvery integration test has a matching YAML fixture file:\n- Test: `qa/integration/specs/dlq_spec.rb`\n- Fixture: `qa/integration/fixtures/dlq_spec.yml`\n\n**Fixture YAML structure:**\n```yaml\n---\nservices:\n  - logstash\n  - elasticsearch\nconfig: |-\n  input { generator { count => 100 } }\n  output { elasticsearch { index => \"test\" } }\n```\n\nFixtures can also use ERB for dynamic values and define multiple named configs as a hash.\n\n**In test code:**\n```ruby\n@fixture = Fixture.new(__FILE__)           # Loads matching YAML\n@logstash = @fixture.get_service(\"logstash\")\nconfig = @fixture.config(\"root\", { :port => 9200 })  # ERB interpolation\n```\n\n### Key Framework Classes\n\n- **`Fixture`** (`framework/fixture.rb`) — Bootstraps services and loads config from YAML fixtures.\n- **`TestSettings`** (`framework/settings.rb`) — Merges per-test YAML with global `suite.yml`, performs ERB expansion.\n- **`LogstashService`** (`services/logstash_service.rb`) — Manages a Logstash process from the built tarball. Key methods: `start_with_config_string(config)`, `start_background(config_file)`, `wait_for_logstash`, `teardown`.\n- **`MonitoringAPI`** (`services/monitoring_api.rb`) — HTTP wrapper for the Logstash REST API (`pipeline_stats`, `node_info`, `health_report`).\n- **Helpers** (`framework/helpers.rb`) — `wait_for_port`, `is_port_open?`, `send_data`, `config_to_temp_file`, `random_port`.\n\n### Service Management\n\nServices are auto-discovered by naming convention (`*_service.rb` in `services/`). Each service consists of:\n1. A Ruby class extending `Service` (e.g. `ElasticsearchService`)\n2. A `<name>_setup.sh` script (called during test setup)\n3. A `<name>_teardown.sh` script (called during cleanup)\n\nTo add a new service: create all three files in `qa/integration/services/` and reference the service name in your fixture YAML.\n\n### Test File Pattern\n\n```ruby\nrequire_relative '../framework/fixture'\nrequire_relative '../framework/settings'\nrequire_relative '../services/logstash_service'\n\ndescribe \"Feature under test\" do\n  before(:all) {\n    @fixture = Fixture.new(__FILE__)\n    @logstash = @fixture.get_service(\"logstash\")\n  }\n\n  after(:all) { @fixture.teardown }\n  after(:each) { @logstash.teardown }\n\n  it \"should behave correctly\" do\n    @logstash.start_with_config_string(@fixture.config)\n    # ... assertions ...\n  end\nend\n```\n\n### Dependencies\n\nDefined in `qa/integration/integration_tests.gemspec`.\n\n## Acceptance Tests (qa/acceptance/)\n\n### Purpose\n\nVerify that Logstash packages (`.deb`, `.rpm`) install, start, and upgrade correctly on target platforms. These test the artifact, not pipeline behavior.\n\nThese tests assume a Linux environment with `/etc/os-release` and distro package tools; they are not meant to run natively on macOS.\n\n### Running\n\n```bash\n# All acceptance tests (from repo root; uses vendored JRuby)\n./gradlew runAcceptanceTests\n\n# Or install gems once, then run a single test directly\n./gradlew installAcceptanceTestGems\ncd qa\nBUNDLE_PATH=vendor/bundle \\\n  ../vendor/jruby/bin/jruby -S bundle exec rspec acceptance/spec/lib/artifact_operation_spec.rb\n```\n\n### Architecture\n\nTests use RSpec `shared_examples` for reusable test sequences:\n- `\"installable\"` — Download, install, start, stop\n- `\"installable_with_jdk\"` — Install and verify bundled JDK\n- `\"runnable\"` — Verify process is running\n- `\"updated\"` — Test upgrade from previous version\n\nShared examples live in `qa/acceptance/spec/shared_examples/`.\n\n## Docker Tests (qa/docker/)\n\n### Purpose\n\nVerify Docker image build correctness (labels, working directory, architecture), container startup, health checks, and X-Pack features.\n\n### Running\n\nThe tests find images by searching local Docker for `docker.elastic.co/logstash/logstash-{flavor}:{version}-SNAPSHOT`. Build the image first, then run tests. `ARCH` defaults to `x86_64`; set `ARCH=aarch64` when building arm64 images. If you run tests against an image whose architecture differs from the host, set `DOCKER_ARCHITECTURE=amd64|arm64` so the spec helpers compare against the correct target architecture.\n```bash\n# Build images (from repo root)\n./gradlew artifactDocker         # full\n./gradlew artifactDockerOss      # oss\n./gradlew artifactDockerWolfi    # wolfi\n./gradlew artifactDockerIronbank # ironbank\n\n# Run tests through Gradle (build + vendored JRuby)\n./gradlew runAllDockerTests\n./gradlew runDockerFullTests     # or runDockerOssTests / runDockerWolfiTests / runDockerIronbankTests\n\n# Or run directly from qa after installAcceptanceTestGems\ncd qa\nBUNDLE_PATH=vendor/bundle \\\n  ../vendor/jruby/bin/jruby -S bundle exec rspec docker/spec/full\n```\n\n### Architecture\n\nTests use shared examples across image flavors (`full`, `oss`, `wolfi`, `ironbank`). Key helpers in `spec/spec_helper.rb`: `find_image`, `start_container`, `exec_in_container`, `wait_for_logstash`, `cleanup_container`.\n\n## Debugging\n\n```bash\n# Integration: verbose output\ncd qa/integration\nVERBOSE=true BUNDLE_PATH=../../build/qa/integration/vendor \\\n  ../../vendor/jruby/bin/jruby -S bundle exec rspec specs/my_spec.rb -fd\n\n# Docker: inspect container\ndocker run -it logstash-test /bin/bash\n```\n","qa/CLAUDE.md":"See `AGENTS.md`.\n","tools/benchmark-cli/README.md":"### Benchmark CLI\n\n#### Build\n\nTo build a self-contained archive of the benchmark tool simply run:\n\n```bash\ngradle clean assemble\n```\n\nwhich will create the output jar under `build/libs/benchmark-cli.jar`.\n\n#### Running\n\n```bash\n$ java -cp 'benchmark-cli.jar:*' org.logstash.benchmark.cli.Main --help\nOption                           Description\n------                           -----------\n--config <File>                  Path to custom logstash config. Required if\n                                   testcase is set to 'custom'\n--data <File>                    Path to custom logstash data. Only if testcase\n                                   is set to 'custom'\n--distribution-version <String>  The version of a Logstash build to download\n                                   from elastic.co.\n--elasticsearch-export <String>  Optional Elasticsearch host URL to store\n                                   detailed results at. (default: )\n--git-hash <String>              Either a git tree (tag/branch or commit hash),\n                                   optionally prefixed by a Github username,\n                                 if ran against forks.\n                                 E.g.\n                                   'ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7',\n                                 'user-\n                                   name#ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7'\n                                   or 'main'\n--local-path <String>            Path to the root of a local Logstash\n                                   distribution.\n                                  E.g. `/opt/logstash`\n--ls-batch-size <Integer>        Logstash batch size (-b argument) to\n                                   configure. (default: 128)\n--ls-workers <Integer>           Number of Logstash worker threads (-w\n                                   argument) to configure. (default: 2)\n--repeat-data <Integer>          Sets how often the test's dataset should be\n                                   run. (default: 1)\n--testcase <String>              Currently available test cases are 'baseline',\n                                   'apache' and 'custom'. (default: baseline)\n--workdir <File>                 Working directory to store cached files in.\n                                   (default: /Users/d059176/.logstash-\n                                   benchmarks) \n```\n\n##### Example\n\n```bash\n$ java -cp 'benchmark-cli.jar:*' org.logstash.benchmark.cli.Main --workdir=/tmp/benchmark2 --testcase=baseline --distribution-version=5.5.0  \n  ██╗      ██████╗  ██████╗ ███████╗████████╗ █████╗ ███████╗██╗  ██╗          \n  ██║     ██╔═══██╗██╔════╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║  ██║          \n  ██║     ██║   ██║██║  ███╗███████╗   ██║   ███████║███████╗███████║          \n  ██║     ██║   ██║██║   ██║╚════██║   ██║   ██╔══██║╚════██║██╔══██║          \n  ███████╗╚██████╔╝╚██████╔╝███████║   ██║   ██║  ██║███████║██║  ██║          \n  ╚══════╝ ╚═════╝  ╚═════╝ ╚══════╝   ╚═╝   ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝          \n                                                                               \n  ██████╗ ███████╗███╗   ██╗ ██████╗██╗  ██╗███╗   ███╗ █████╗ ██████╗ ██╗  ██╗\n  ██╔══██╗██╔════╝████╗  ██║██╔════╝██║  ██║████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝\n  ██████╔╝█████╗  ██╔██╗ ██║██║     ███████║██╔████╔██║███████║██████╔╝█████╔╝ \n  ██╔══██╗██╔══╝  ██║╚██╗██║██║     ██╔══██║██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗ \n  ██████╔╝███████╗██║ ╚████║╚██████╗██║  ██║██║ ╚═╝ ██║██║  ██║██║  ██║██║  ██╗\n  ╚═════╝ ╚══════╝╚═╝  ╚═══╝ ╚═════╝╚═╝  ╚═╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝\n                                                                               \n  ------------------------------------------\n  Benchmarking Version: 5.5.0\n  Running Test Case: baseline\n  ------------------------------------------\n  Start Time: Sat 7 22 21:28:45.4 2017 CEST\n  Statistical Summary:\n  \n  Elapsed Time: 33s\n  Num Events: 977816\n  Throughput Min: 2000.00\n  Throughput Max: 44500.00\n  Throughput Mean: 37608.31\n  Throughput StdDev: 8985.03\n  Throughput Variance: 80730818.62\n  Mean CPU Usage: 19.27%\n```\n","tools/dependencies-report/README.md":"# Dependency audit tool\n\nThe dependency audit tool automates the verification of the following criteria for all\nthird-party dependencies that are shipped as part of either Logstash core or the [default Logstash \nplugins](https://github.com/elastic/logstash/blob/main/rakelib/plugins-metadata.json):\n* The dependency has been added to the [dependency list file](https://github.com/elastic/logstash/blob/main/tools/dependencies-report/src/main/resources/licenseMapping.csv)\nwith an appropriate project URL and [SPDX license identifier](https://spdx.org/licenses/). \n* The license for the dependency is among those [approved for distribution](https://github.com/elastic/logstash/blob/main/tools/dependencies-report/src/main/resources/acceptableLicenses.csv).\n* There is a corresponding `NOTICE.txt` file in the [notices folder](https://github.com/elastic/logstash/tree/main/tools/dependencies-report/src/main/resources/notices)\ncontaining the appropriate notices or license information for the dependency. These individual \nnotice files will be combined to form the notice file shipped with Logstash.\n\nThe dependency audit tool enumerates all the dependencies, Ruby and Java, direct and transitive,\nfor Logstash core and the default plugins. If any dependencies are found that do not conform to\nthe criteria above, the name of the dependency(ies) along with instructions for resolving are \nprinted to the console and the tool exits with a non-zero return code.\n\nThe dependency audit tool should be run using the script in the `bin` folder:\n\n`$LS_HOME/bin/dependencies-report --csv report.csv`\n","tools/jvm-options-parser/README.md":"Standalone jar for JvmOptionsParser to enable it to run under Java 8 to provide\nhelpful fail fast message when logstash is run older versions of Java.","tools/logstash-docgen/README.md":"**Features**\n\n## Generating the documentation for a Logstash instance\n\nYou can now generate the documentation for a Logstash distribution.\n\n*prerequisite*:\n\n- You need to have installed the plugins inside Logstash, this is necessary to pickup the right file when generating the documentation.\n- You need to have the development gems installed. `bin/logstash-plugin --no-verify --development`\n\nIf you have all of the above you can run this command:\n\n```sh\nrake doc:generate-plugins\nrake doc:generate-plugins[/tmp/the-new-doc] # Depending on the shell you are using you might need to quote the task name, like this rake \"doc:generate-plugins[/tmp/new-doc]\"\n```\n\nThis will give you a output similar to this\n\n```sh\nlogstash-input-file > SUCCESS\nlogstash-input-s3 > SUCCESS\nlogstash-input-kafka > FAIL\n[...]\nExceptions: XXXX\n```\n\nThe generator will try to generate the doc for all the plugins defined in the *Gemfile* and installed in Logstash, if anything goes wrong it won't\nstop the generation of the other plugin. The Task will also report any errors with stacktraces at the end, if one plugin fail the build,\nyou can interrupt the process and it will output the current errors before exiting.\n\n\n## Generating the documentation for all the plugins from the organization\n\nYou can now generate the documentation from main for all the plugin in the *logstash-plugins* organization.\n\n*prerequisite*\n\n- You need to go in the `tools/logstash-docgen` directory\n- You need to have the dependency installed.\n\n To get started you can run the following commands:\n\n ```sh\n cd tools/logstash-docgen\n bundle install\n ```\n\nYou can use the `bin/logstash-docgen` command to generate any plugin that you want, this executable can generate all the plugins or specific one from their main branch.\n\nUsages:\n\n```sh\nbin/logstash-docgen --all # will generate the doc for all the plugins\nbin/logstash-docgen logstash-input-file logstash-input-s3 # generate doc for 2 plugins\n```\n\n**See:** `bin/logstash-docgen --help` for complete usage.\n\n**Notes:**\n- The nature of theses tasks require a lot of external execution to make sure all the doc are done in isolation, this process can take a long time.\n- Some plugins will be skipped, see `logstash-docgen.yml` for details.\n- The script will inject itself as a dependency on the plugin.\n\n\n## Testing the documentation as the plugin author\n\n*prerequisite*\n\n- Declare the `logstash-docgen` as development dependency\n- Add `require \"logstash/docgen/plugin_doc\"` to the `Rakefile`\n- Run `bundle install`\n\nAfter you can have access to a few rake tasks, you can list them with `bundle exec rake -T`\n\n```\nbundle exec rake doc:asciidoc # return the raw asciidoc\nbundle exec rake doc:html # Give you the raw html\n```\n\n## CI test\n\nThe CI can use the `ci/docs.sh` script to correctly bootstrap and execute the docgeneration\n\n\n","tools/paquet/README.md":"# Paquet\n\nThis gem allow a developer to create a uber gem, a uber gem is a gem that content the current gem and his dependencies and is distributed as a tarball.\n\nThis tool allow to define what will be bundler and what should be ignored, it uses the dependencies defined in the gemspec and gemfile to know what to download.\n\nNote that by default no gems will be bundled.\n\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'paquet'\n```\n\nAnd then execute:\n\n    $ bundle\n\n## Usage\nDefine the dependencies in your Rakefile\n\n```ruby\nrequire \"paquet\"\n\nTARGET_DIRECTORY = File.join(File.dirname(__FILE__), \"dependencies\")\n\nPaquet::Task.new(TARGET_DIRECTORY) do\n  pack \"manticore\"\n  pack \"launchy\"\n  pack \"gemoji\"\n  pack \"logstash-output-elasticsearch\"\n\n  # Everything not defined here will be assumed to be provided\n  # by the target installation\n  ignore \"logstash-core-plugin-api\"\n  ignore \"logstash-core\"\nend\n```\n\nAnd run\n\n```\nbundle exec rake paquet:vendor\n```\n\nThe dependencies will be downloaded in your target directory.\n\n## Project Principles\n\n* Community: If a newbie has a bad time, it's a bug.\n* Software: Make it work, then make it right, then make it fast.\n* Technology: If it doesn't do a thing today, we can make it do it tomorrow.\n\n## Contributing\n\nAll contributions are welcome: ideas, patches, documentation, bug reports,\ncomplaints, and even something you drew up on a napkin.\n\nProgramming is not a required skill. Whatever you've seen about open source and\nmaintainers or community members  saying \"send patches or die\" - you will not\nsee that here.\n\nIt is more important to me that you are able to contribute.\n\nFor more information about contributing, see the\n[CONTRIBUTING](../CONTRIBUTING.md) file.\n","x-pack/AGENTS.md":"# x-pack/AGENTS.md\n\nGuidance for coding agents working with X-Pack (Elastic-licensed) features. See also the root `AGENTS.md` for general project conventions.\n\n## Overview\n\nX-Pack provides commercial features that extend the Logstash core. Most features integrate via the **UniversalPlugin** extension pattern; License Checking is a standalone library used by the other features. X-Pack is a separate Ruby package under `x-pack/` with its own test suite. Build without X-Pack by setting `OSS=true`.\n\n## Features\n\n| Feature | Path | Purpose |\n|---------|------|---------|\n| **Monitoring** | `lib/monitoring/` | Collects JVM, system, and pipeline metrics; ships to Elasticsearch |\n| **Config Management** | `lib/config_management/` | Fetches pipeline configs from Elasticsearch/Kibana |\n| **GeoIP Database Management** | `lib/geoip_database_management/` | Auto-downloads and updates GeoIP databases from Elastic CDN |\n| **License Checking** | `lib/license_checker/` | Validates Elasticsearch license for feature gating |\n\n## Extension Pattern\n\nMonitoring, Config Management, and GeoIP follow the same integration pattern. Each has an **extension class** inheriting from `LogStash::UniversalPlugin` that implements two methods:\n\n1. **`additionals_settings(settings)`** — Registers `xpack.*` configuration settings with the core settings registry.\n2. **`register_hooks(hooks)`** — Registers lifecycle callbacks with `LogStash::Runner` (e.g. `before_bootstrap_checks`, `after_bootstrap_checks`).\n\n**Entry point:** `lib/x-pack/logstash_registry.rb` registers all three extensions plus built-in input/output plugins with `LogStash::PLUGIN_REGISTRY`.\n\n**Extension files:**\n- `lib/config_management/extension.rb`\n- `lib/geoip_database_management/extension.rb`\n- `lib/monitoring/monitoring.rb` (extension at bottom of file)\n\n### Adding New Settings\n\nUse `LogStash::Setting::*` classes in `additionals_settings`:\n\n```ruby\ndef additionals_settings(settings)\n  settings.register(LogStash::Setting::BooleanSetting.new(\"xpack.feature.enabled\", false))\n  settings.register(LogStash::Setting::TimeValueSetting.new(\"xpack.feature.interval\", \"5s\"))\n  settings.register(LogStash::Setting::ArrayCoercible.new(\"xpack.feature.hosts\", String, [\"localhost\"]))\n  settings.register(LogStash::Setting::NullableStringSetting.new(\"xpack.feature.password\"))\nend\n```\n\nAll X-Pack settings use the `xpack.` prefix. Elasticsearch connection options (hosts, SSL, auth, proxy) are shared across features via the `ElasticsearchOptions` helper mixin.\n\n## License Checking\n\nFeatures that require a commercial license use the `Licensed` mixin (`lib/license_checker/licensed.rb`):\n\n1. Call `setup_license_checker(FEATURE_NAME)` during initialization.\n2. Wrap feature logic in `with_license_check(raise_on_error) { ... }`.\n3. Override `populate_license_state(xpack_info, is_serverless)` to return `{ :state => :ok | :error, :log_level => ..., :log_message => ... }`.\n\nThe `LicenseManager` (`lib/license_checker/license_manager.rb`) polls Elasticsearch every 30 seconds and notifies observers on state changes. License types: `trial`, `basic`, `standard`, `gold`, `platinum`, `enterprise`. Config management requires trial or above (not basic).\n\n## Running Tests\n\n```bash\n# Unit tests (from repo root)\n./gradlew :logstash-xpack:rubyTests\n\n# Integration tests (requires running Elasticsearch)\n./gradlew :logstash-xpack:rubyIntegrationTests\n\n# Single integration spec\n./gradlew :logstash-xpack:rubyIntegrationTests \\\n  -PrubyIntegrationSpecs=qa/integration/management/multiple_pipelines_spec.rb\n```\n\n### Test Structure\n\n- **Unit specs:** `x-pack/spec/` — Organized by feature (`spec/monitoring/`, `spec/config_management/`, `spec/geoip_database_management/`, `spec/license_checker/`).\n- **Integration tests:** `x-pack/qa/integration/` — Subdirectories for `management/`, `monitoring/`, and `fips-validation/`.\n- **Test helpers:** `x-pack/spec/support/helpers.rb` and `x-pack/spec/support/matchers.rb`.\n- **Test runners:** JUnit-based RSpec invokers in `x-pack/src/test/java/org/logstash/xpack/test/` (`RSpecTests.java` for unit, `RSpecIntegrationTests.java` for integration).\n\n### GeoIP Test Data\n\nThe `unzipGeolite` Gradle task downloads GeoLite2 test databases. Unit tests for GeoIP depend on this task running first (handled automatically by the `rubyTests` dependency chain).\n\n## Key Architectural Patterns\n\n- **Hook-based integration.** Config management intercepts bootstrap checks to swap the local config source with an Elasticsearch source. Monitoring adds an internal pipeline after the agent starts. Neither modifies core code directly.\n- **Singleton managers.** `GeoipDatabaseManagement::Manager` and `LicenseChecker::LicenseManager` are singletons with thread-safe initialization via Mutex.\n- **Observer pattern.** Database subscriptions and license managers notify observers on state changes, enabling features to react dynamically without polling.\n- **Internal pipelines.** Monitoring generates its pipeline config from an ERB template (`lib/template.cfg.erb`) and injects it via `InternalPipelineSource`. These run alongside user pipelines but are hidden from the user-facing API.\n","x-pack/CLAUDE.md":"See `AGENTS.md`.\n"},"files":{"AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to coding agents when working with code in this repository.\n\n## Project Overview\n\nLogstash is a server-side data processing pipeline that ingests data from multiple sources, transforms it, and sends it to various destinations. It's part of the Elastic Stack and is a **hybrid Java/Ruby application** using JRuby.\n\n### JRuby and versions\n\n- The JRuby version is defined in **`versions.yml`** (repo root). The project uses **vendored JRuby only** — no external Ruby, rvm, or rbenv is required; Gradle installs JRuby into `vendor/jruby` via the bootstrap task.\n- The gem path includes a Ruby compatibility version segment (e.g. `vendor/bundle/jruby/X.Y.0`). It is intentionally hardcoded in Gradle (`rubyUtils.gradle`), shell scripts (`bin/logstash.lib.sh`), and Java test helpers (e.g. `RubyEnvTestCase`). To find the current value, grep for `vendor/bundle/jruby/` in `bin/logstash.lib.sh`. Do not treat it as a bug or \"dynamicize\" it when working on the codebase.\n\n### Single source of truth for versions\n\n- **`versions.yml`** in the repository root is the single source of truth for Logstash version, JRuby version, bundled JDK, Jackson, and related coordinates. Gradle loads it in `settings.gradle`; the `logstash-core` and `logstash-core-plugin-api` gemspecs copy from it. When in doubt about a version, check or update `versions.yml`.\n\n## Build Commands\n\nRun all build and test commands from the **repository root** (where `gradlew` and `versions.yml` live), unless a specific task or doc says otherwise.\n\nAll commands require a JDK that meets the `sourceCompatibility` defined in `build.gradle` (currently 17+). JDK 21 is preferred and is the version bundled for distribution (see `bundled_jdk` in `versions.yml`). Ruby tests run via vendored JRuby — no external Ruby, rvm, or rbenv needed.\n\nUse Gradle tasks as the normal entry points from the repo root. Rake and bundle are used as leaf tools where the existing Gradle tasks or scripts already wire them up.\n\n```bash\n# Initial setup (required once)\n./gradlew bootstrap\n\n# Install development dependencies\n./gradlew installDevelopmentGems\n\n# Install default plugins (80+ plugins)\n./gradlew installDefaultGems\n\n# Build distribution artifact (tar.gz)\n./gradlew assembleTarDistribution\n# Other distribution formats (zip, deb, rpm) are built via rake tasks in rakelib/artifacts.rake\n```\n\n**Build performance (CI):** For faster `installDefaultGems`, use `JARS_SKIP=true`; required jars are already vendored or in JRuby stdlib.\n\n## Running Tests\n\n**Test layout:** Unit specs live under **`spec/`** (root) and **`logstash-core/spec/`**. Integration and acceptance tests live under **`qa/`** (e.g. **`qa/integration`** for integration tests). Use the commands below from the repo root; the single-integration-test example uses a path under `qa/integration`.\n\n**Test task structure:** There are two separate Gradle test tasks: `javaTests` (pure Java unit tests) and `rubyTests` (Ruby specs **and** Java tests that depend on Ruby infrastructure). The top-level `test` task excludes all classes itself and simply delegates to both, so `./gradlew test --tests ...` will silently match nothing. Always apply `--tests` to `javaTests` or `rubyTests` directly.\n\n**`javaTests` vs `rubyTests` split:** Several Java test classes are excluded from `javaTests` and included in `rubyTests` because they load Ruby code at runtime. The exclude/include lists are in `logstash-core/build.gradle`. If `--tests` returns \"No tests found for given includes\", the class is likely in the other task. Examples of Java tests that live in `rubyTests`:\n- `OutputDelegatorTest`, `JavaCodecDelegatorTest` — test delegators that wrap Ruby plugin instances\n- `ConfigCompilerTest`, `CompiledPipelineTest`, `EventConditionTest` — test config compilation which invokes the Ruby parser\n- `PluginFactoryExtTest` — tests Ruby plugin instantiation\n\n```bash\n# All unit tests (Java + Ruby)\n./gradlew test\n\n# All Java tests (pure Java, no Ruby dependency)\n./gradlew :logstash-core:javaTests\n\n# Single Java test class\n./gradlew :logstash-core:javaTests --tests org.logstash.TimestampTest\n\n# Single Java test method\n./gradlew :logstash-core:javaTests --tests org.logstash.TimestampTest.testEpochMillis\n\n# Java tests by pattern\n./gradlew :logstash-core:javaTests --tests \"org.logstash.settings.*\"\n\n# WRONG — will silently run nothing because `test` excludes all classes:\n# ./gradlew :logstash-core:test --tests org.logstash.TimestampTest\n\n# Java test that depends on Ruby — must use rubyTests, not javaTests:\n./gradlew :logstash-core:rubyTests --tests org.logstash.config.ir.compiler.OutputDelegatorTest\n\n# All Ruby tests (core) — also runs the Ruby-dependent Java tests listed above\n./gradlew :logstash-core:rubyTests\n\n# Single Ruby spec file\nSPEC_OPTS=\"-fd -P logstash-core/spec/logstash/agent_spec.rb\" \\\n  ./gradlew :logstash-core:rubyTests --tests org.logstash.RSpecTests\n\n# All integration tests\n./gradlew runIntegrationTests\n\n# Single integration test\n./gradlew runIntegrationTests -PrubyIntegrationSpecs=specs/slowlog_spec.rb\n\n# All X-Pack Ruby tests\n./gradlew :logstash-xpack:rubyTests\n\n# Single X-Pack Ruby test\nSPEC_OPTS=\"-fd -P spec/config_management/elasticsearch_source_spec.rb\" \\\n  ./gradlew :logstash-xpack:rubyTests --rerun-tasks\n\n# Single Ruby test for core or X-Pack (fast, direct JRuby)\nbin/rspec logstash-core/spec/logstash/some_spec.rb\nbin/rspec x-pack/spec/config_management/elasticsearch_source_spec.rb\n```\n\n## Quick Verification\n\n```bash\n# Start Logstash with inline config\nbin/logstash -e 'input { stdin { } } output { stdout {} }'\n```\n\n## Architecture\n\n### Dual Java/Ruby Runtime\n- **Java Core** (`logstash-core/src/main/java/org/logstash/`): Pipeline execution engine, event processing, persistent queue, configuration compilation\n- **Ruby Core** (`logstash-core/lib/logstash/`): Agent lifecycle, plugin coordination, REST API, configuration loading, metric store, settings management\n\n### Key Components\n- **Agent** (`logstash-core/lib/logstash/agent.rb`): Main orchestrator, manages pipeline lifecycle\n- **JavaPipeline** (`logstash-core/lib/logstash/java_pipeline.rb`): Ruby class for core event processing with worker threads, backed by Java execution infrastructure in `logstash-core/src/main/java/org/logstash/execution/`\n- **Plugin System** (`logstash-core/lib/logstash/plugins/`): Input/Filter/Output/Codec plugin infrastructure\n- **Plugin API** (`logstash-core-plugin-api/`): Public interface for plugin developers\n- **JvmOptionsParser** (`tools/jvm-options-parser/`): Standalone Java 11 subproject that parses `config/jvm.options` and composes JVM command-line flags before JRuby starts. Supports version-conditional syntax (e.g. `11-:`, `16-20:`), resolves `${ENV_VAR}` references, and enforces mandatory JVM flags. Invoked from `bin/logstash.lib.sh` during `setup_java()`.\n\n### Directory Structure\n- `logstash-core/` - Core framework (Java + Ruby)\n- `lib/pluginmanager/` - Plugin installation and management\n- `x-pack/` - Elastic-licensed features (disable with `OSS=true`)\n- `qa/` - Quality assurance: acceptance, integration, Docker tests\n- `tools/` - Development tools (docgen, benchmarks, release automation)\n\n### Gradle–Ruby interaction\n\n- **`rubyUtils.gradle`** drives Ruby from Gradle: it uses JRuby's `ScriptingContainer`, `executeJruby`, and invokes bundle/rake. The vendored JRuby lives under **`vendor/jruby`** after `./gradlew bootstrap`. When debugging Gradle tasks that run Ruby (e.g. plugin install, Rake), this is where the wiring lives.\n\n## Code Design Practices\n\nWhen modifying code, don't just append to what's there — evaluate whether the surrounding code still makes sense with the change. If a method, class, or abstraction was designed for one case and you're adding a second, consider restructuring so the result reads as if both cases were planned from the start. Each distinct concept should have its own clearly-named unit, and higher-level code should compose those units so the intent is obvious without reading the implementation.\n\n### Plugin System Conventions\n\n- **Inherit from the type-specific base class.** Each plugin type has a base in `logstash-core/lib/logstash/{type}s/base.rb` (inputs, filters, outputs, codecs). Override `register` for setup plus the type-specific entry point: `run` (inputs), `filter` (filters), `receive`/`multi_receive` (outputs), `decode`/`encode` (codecs).\n- **Declare configuration with the `config` DSL**, not raw `@params` access. Use `config :name, :validate => :type, :default => value` — defined in `logstash-core/lib/logstash/config/mixin.rb`. The framework handles validation, coercion, and documentation generation from these declarations.\n- **Set output concurrency at the class level** with `concurrency :shared` or `concurrency :single` in `logstash-core/lib/logstash/outputs/base.rb`. This determines which `OutputStrategy` the pipeline uses to wrap the plugin: `:shared` means multiple worker threads can call the plugin simultaneously; `:single` serialises all calls so only one thread executes the plugin at a time. Most new outputs should use `:shared`.\n- **Plugin lookup goes through `LogStash::Plugins::Registry#lookup`** (`logstash-core/lib/logstash/plugins/registry.rb`). New built-in plugins register in `logstash-core/lib/logstash/plugins/builtin.rb`. External plugins are discovered as gems.\n\n### Delegator and Metrics Patterns\n\n- **Every plugin runs behind a delegator** that adds metrics collection and thread-safety. Filters: `FilterDelegatorExt` (Java), outputs: `OutputDelegatorExt` (Java), codecs: `Codecs::Delegator` (`logstash-core/lib/logstash/codecs/delegator.rb`). When changing plugin behavior, determine whether the change belongs in the delegator (cross-cutting concern) or in the plugin itself.\n- **Metrics follow a namespace hierarchy.** Use `metric.namespace(:sub)` to create children. `NullMetric` implements the same interface but discards data — it's the null-object stand-in when `enable_metric => false` or during testing. Never instantiate metric objects directly; accept them from the framework.\n- **Standard mixins are included automatically.** All plugins get `LogStash::Util::Loggable` (provides `logger`, `slow_logger`, `deprecation_logger`) and `LogStash::Config::Mixin` (config DSL). Additional mixins like `ECSCompatibilitySupport` and `EventFactorySupport` are opt-in per plugin.\n\n### Pipeline Execution Model\n\n- **The worker loop is the hot path.** `WorkerLoop` (`logstash-core/src/main/java/org/logstash/execution/WorkerLoop.java`) pulls batches from the queue, runs them through the compiled pipeline, and signals completion. Shutdown is coordinated via `AtomicBoolean` flags — do not add blocking calls or heavy allocations inside this loop.\n- **Pipeline lifecycle follows a strict ordering** in `JavaPipeline` (`logstash-core/lib/logstash/java_pipeline.rb`): outputs and filters register first, then workers start, then inputs start last. On shutdown the reverse applies: inputs stop first, workers drain, then filters and outputs close. Violating this ordering causes data loss or deadlocks.\n- **Use `Concurrent::AtomicBoolean` (Ruby) or `java.util.concurrent.atomic.AtomicBoolean` (Java) for cross-thread signaling.** Do not use plain boolean instance variables for state shared across threads — Ruby's GIL does not guarantee visibility, and JRuby runs without one.\n\n#### Runtime Code Generation (Config → Java Classes)\n\nLogstash configs are compiled into executable Java classes at runtime through a multi-stage pipeline:\n\n1. **Parse:** Treetop grammar (`logstash-core/lib/logstash/compiler/lscl/lscl_grammar.treetop`) parses `.conf` files into an AST.\n2. **IR:** `ConfigCompiler` (`logstash-core/src/main/java/org/logstash/config/ir/ConfigCompiler.java`) converts the AST into a `PipelineIR` — a directed acyclic graph of plugins and conditionals.\n3. **Compile:** `DatasetCompiler` (`logstash-core/src/main/java/org/logstash/config/ir/compiler/DatasetCompiler.java`) generates Java source strings for each pipeline vertex. `ComputeStepSyntaxElement` (`logstash-core/src/main/java/org/logstash/config/ir/compiler/ComputeStepSyntaxElement.java`) compiles these sources into classes via Janino at runtime. Generated classes live under the `org.logstash.generated` package.\n4. **Execute:** `CompiledPipeline` (`logstash-core/src/main/java/org/logstash/execution/CompiledPipeline.java`) wires the compiled datasets into the worker loop.\n\nConditionals from config (`if [field] == \"value\"`) compile into `EventCondition` lambdas wired into `SplitDataset` nodes. To inspect generated source code, add these flags to `config/jvm.options`:\n```\n-Dorg.codehaus.janino.source_debugging.enable=true\n-Dorg.codehaus.janino.source_debugging.dir=/path/to/output\n```\n\n### Java Code Conventions\n\n- **Packages are organized by functional domain**, not architecture layers. Main packages under `org.logstash`: `execution` (pipeline/workers), `config.ir` (config compilation), `plugins` (plugin infrastructure), `ackedqueue` (persistent queue), `instrument.metrics` (metrics), `ext` (JRuby extensions), `settings` (configuration types), `log` (logging). Place new classes in the package matching their domain.\n- **Naming conventions:** JRuby extension classes use the `Ext` suffix (`LoggableExt`, `AbstractSimpleMetricExt`). Delegator classes use `Delegator` suffix. Abstract bases use the `Abstract` prefix. Most concrete classes are `public final class` — use plain `public class` only when subclassing is intended (e.g. `BaseSetting<T>`).\n- **Prefer unchecked exceptions.** Custom exceptions extend `RuntimeException` (e.g. `FieldReference.IllegalSyntaxException`, `Event.InvalidTagsTypeException`). Checked exceptions appear only when unavoidable (`IOException`, `InterruptedException`). Include descriptive messages and preserve exception chains via the `cause` parameter.\n- **Null handling:** Use `Objects.requireNonNull()` for constructor parameter validation. Use `@Nullable` annotation when null is an intentional part of the contract. Use `Optional` for parse/lookup chains that may fail (see `Timestamp.tryParse`), not as a general null replacement. Prefer explicit null checks before dereference over implicit assumptions.\n- **Logging uses Log4j 2.** Declare loggers as `private static final Logger LOGGER = LogManager.getLogger(MyClass.class)`. Use `TRACE` for diagnostic detail (parse attempts, fallback paths), `WARN` for recoverable issues, `ERROR` for uncaught exceptions, `FATAL` for unrecoverable system errors. Include context (thread name, field type, value) in messages.\n- **Thread safety:** Prefer immutable `final` fields as the primary safety mechanism. Use `ConcurrentHashMap` for thread-safe caches (see `FieldReference.CACHE`). Use `AtomicBoolean` for shutdown signaling. Document thread-safety guarantees in class-level JavaDoc when the class is shared across threads.\n\n### REST API Conventions\n\n- **API modules inherit from `LogStash::Api::Modules::Base`** (`logstash-core/lib/logstash/api/modules/base.rb`), which extends `Sinatra::Base`. Each module receives the `agent` reference and a `CommandFactory` in its constructor. Define routes using Sinatra's `get`/`put` DSL inside the module class.\n- **Routing uses rack namespace mapping.** Endpoints are mounted at path prefixes (`/_node`, `/_stats`, `/_node/stats`, `/_node/plugins`, `/_node/logging`, `/_health_report`) defined in `rack_namespaces` in `logstash-core/lib/logstash/api/rack_app.rb`. Add new endpoint namespaces there, not via ad-hoc Sinatra routing.\n- **All responses go through `respond_with`** (`logstash-core/lib/logstash/api/app_helpers.rb`). It automatically wraps JSON responses with default metadata (host, version, http_address, id) unless `exclude_default_metadata` is passed. Use `respond_with(data)` — do not write raw JSON responses.\n- **Errors use `ApiError` subclasses** (`logstash-core/lib/logstash/api/errors.rb`): `NotFoundError` (404), `BadRequest` (400), `RequestTimeout` (408). Raise these from route handlers; the base module catches them and formats a consistent error envelope with `path`, `status`, and `error` fields.\n- **Business logic lives in command classes**, not in route handlers. Commands inherit from `LogStash::Api::Commands::Base` (`logstash-core/lib/logstash/api/commands/base.rb`) and access metrics via `service.extract_metrics(path, *keys)`. Route handlers should only call `factory.build(:command_name)` and pass results to `respond_with`.\n\n### Java/Ruby Boundary\n\n- **JRuby extensions use `@JRubyClass`/`@JRubyMethod` annotations.** Classes register in `RubyUtil` (`logstash-core/src/main/java/org/logstash/RubyUtil.java`) via `setupLogstashClass` or `defineClassUnder`. Follow existing patterns in `RubyUtil`'s static initializer when adding new Java-backed Ruby classes.\n- **`ThreadContext` must be obtained at point of use** via `RubyUtil.RUBY.getCurrentContext()`, never stored at construction time. Storing it causes `NullPointerException` when used from a different thread (JRuby 10+). See `RubyCodecDelegator` for the correct pattern.\n- **Test the round-trip through Java.** Integration tests should exercise the full path from Ruby through the Java layer and back, not just the Ruby classes in isolation. This catches type coercion issues and metric namespace registration bugs that unit tests miss.\n- **Java tests that load Ruby:** Some Java tests (e.g. `RubyEnvTestCase` and subclasses) set **GEM_HOME** / **GEM_PATH** to the vendored bundle path (`vendor/bundle/jruby/<ruby-compat-version>`) so they can load Logstash Ruby code. Check `bin/logstash.lib.sh` or `rubyUtils.gradle` for the current version segment. That path is intentional; do not change it when touching those tests.\n\n### Test Practices\n\n- **Use `sample_one` helper for filter pipeline tests.** `PipelineHelpers#sample_one` (`logstash-core/spec/support/pipeline/pipeline_helpers.rb`) spins up a real pipeline, feeds events through a filter config, and captures results — preferred over manually constructing pipeline objects.\n- **Create dummy plugin classes inside spec files for isolation.** Define throwaway classes inheriting from the appropriate base, register with `LogStash::PLUGIN_REGISTRY.add`. See `pipeline_helpers.rb` for examples.\n- **Use `shared_examples` for interface contracts and `shared_context` for common setup.** Examples: `\"metrics commons operations\"` in `logstash-core/spec/support/shared_examples.rb`, `\"execution_context\"` in `logstash-core/spec/support/shared_contexts.rb`.\n- **Integration tests use the Fixture + Service pattern.** `Fixture` (`qa/integration/framework/fixture.rb`) bootstraps services and loads config from YAML fixture files under `qa/integration/fixtures/`.\n- **Test that existing rescue blocks catch new code paths.** When a method has a `rescue => e` that returns `nil`, verify that exceptions raised in newly added branches are caught by it. It is easy to accidentally add code outside the `begin...rescue` scope.\n- **Clean up JVM state in `after` blocks.** Any `java.lang.System.setProperty` call in a test must have a corresponding `clearProperty` in `after` to avoid leaking state to other specs.\n- **Prefer concrete test doubles over mocks.** The codebase uses purpose-built classes (`DummyInput`, `DummyFilter`, `DummyOutput` in `logstash-core/spec/support/mocks_classes.rb`) that inherit from real base classes with minimal implementations. Use these over RSpec `double()` when testing pipeline behavior — they exercise real plugin lifecycle methods.\n- **Reserve mocks for infrastructure boundaries.** `LogStash::Agent`, loggers, and `LogStash::SETTINGS` are commonly stubbed to prevent real pipeline startup. Use `allow(obj).to receive(:method).and_call_original` when you need the mock for setup but want to test real behavior. The codebase uses plain `double()` and `.as_null_object` — not `instance_double` or `class_double` — due to JRuby's dynamic method dispatch.\n\n### Test Validity\n\n- **Verify every new test fails without the corresponding change.** Before treating a test as done, confirm it fails against the pre-change code. A test that passes with or without the fix proves nothing. This is the most basic check that a test is load-bearing.\n- **Assert on the specific behaviour the change introduces, not on pre-existing conditions.** If the system already returned `200 OK` before your fix, asserting `200 OK` does not validate the fix. Pin the assertion to an observable difference — a new field, a changed value, a previously-missing log line.\n- **For bug fixes, reproduce the bug in the test first.** Write the test, watch it fail for the same reason the bug manifests, then apply the fix and watch it pass. If you cannot make the test fail, either the bug is not what you think or the test is not targeting it.\n- **Beware of tautological mocks.** Stubbing a method to return the value you then assert is a circular proof. Mocks should simulate collaborators, not replace the code under test. If removing the implementation and keeping only the mock still passes the test, the test is testing the mock.\n\n## Plugin Development\n\nPlugins live in separate repositories under [logstash-plugins](https://github.com/logstash-plugins). Each plugin is a self-contained Ruby gem. Plugin types: input, filter, output, codec.\n\n- The **`logstash-core-plugin-api`** version is in **`versions.yml`**; plugins (in separate repos) depend on it.\n- Templates for new plugins are in **`lib/pluginmanager/templates/`**.\n- Supported plugin versions and compatibility are tracked in the [logstash-plugins `.ci`](https://github.com/logstash-plugins/.ci) repo (e.g. `logstash-versions.yml`).\n\n## Sensitive Data Handling\n\nWhen adding configuration parameters for sensitive data:\n- In Ruby: Apply `:password` validation or wrap with `Logstash::Util::Password`\n- In Java: See `Password.java` and `SecretVariable.java` for patterns\n- Override `toString()` to mask sensitive values\n- Never log secrets at any log level by default\n\nExample:\n```ruby\n:my_auth => { :validate => :password }\n# Or wrap manually:\n::LogStash::Util::Password.new(my_auth)\n```\n\n## Environment Variables\n\n```bash\nexport LOGSTASH_SOURCE=1\nexport LOGSTASH_PATH=/path/to/logstash\nexport JAVA_HOME=/path/to/jdk  # JDK 17+ (see build.gradle sourceCompatibility)\nexport OSS=true                 # Build without X-Pack\n```\n\n## Debugging\n\n```bash\n# Attach debugger to running Logstash\nLS_JAVA_OPTS=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005\" bin/logstash ...\n```\n\n## Breaking Changes Policy\n\n1. Implement new behavior as opt-in (MINOR)\n2. Add deprecation warnings (MINOR)\n3. Change default, keep opt-out option (MAJOR)\n4. Remove old behavior (MAJOR)\n\n## Documentation\n\n### Directory Structure\n- `docs/reference/` - Main user-facing documentation (Markdown)\n- `docs/extend/` - Plugin development and contribution guides (Markdown)\n- `docs/release-notes/` - Version-specific release notes, breaking changes, deprecations\n- `docs/static/` - Static assets (images, OpenAPI specs)\n- `tools/logstash-docgen/` - Auto-generates plugin documentation from code\n\n### Formats\n- **Reference docs**: Markdown (`.md`) with front matter and admonition syntax\n- **Plugin docs**: AsciiDoc (`.asciidoc`) with dynamic ID variables\n\n### Markdown Conventions (Reference Docs)\n```markdown\n---\nmapped_pages:\n  - https://www.elastic.co/guide/en/logstash/current/page.html\n---\n\n# Heading Title [anchor-id]\n\n::::{admonition} Note title\n:class: note\nContent here\n::::\n```\n\n### AsciiDoc Conventions (Plugin Docs)\n```asciidoc\n:plugin: example\n:type: filter\n\n[id=\"plugins-{type}s-{plugin}\"]\n=== Example filter plugin\n\n[id=\"plugins-{type}s-{plugin}-options\"]\n==== Configuration Options\n```\n\nUse dynamic IDs (`{type}`, `{plugin}`) to prevent ID duplication across plugin versions.\n\n### Guidelines\n- All PRs must include documentation updates when applicable\n- Plugin documentation is required before merging\n- Follow Elastic documentation standards: one sentence per line in AsciiDoc, use `[id=\"...\"]` anchors (not Markdown-style), prefer `[source,lang]` for code blocks, use `<<anchor>>` for cross-references, and keep titles in title case\n- Images go in `docs/reference/images/` (PNG format)\n\n## See also\n\n- **CONTRIBUTING.md** — Contribution process, sensitive data, and where to ask questions\n- **STYLE.md** — Ruby style (indentation, logging, hash syntax) and consistency\n","CLAUDE.md":"See `AGENTS.md`.\n","qa/AGENTS.md":"# qa/AGENTS.md\n\nGuidance for coding agents working with the QA test infrastructure. See also the root `AGENTS.md` for general project conventions.\n\n## Overview\n\nThe `qa/` directory contains three independent test suites:\n\n| Suite | Path | Purpose |\n|-------|------|---------|\n| **Integration** | `qa/integration/` | End-to-end tests with live services (Elasticsearch, Kafka, etc.) |\n| **Acceptance** | `qa/acceptance/` | Package artifact testing (deb, rpm install/upgrade) |\n| **Docker** | `qa/docker/` | Docker image and container verification |\n\nEach suite has its own dependencies and execution workflow.\n\n## Integration Tests (qa/integration/)\n\n### Running\n\n```bash\n# All integration tests via Gradle (handles all setup automatically)\n./gradlew runIntegrationTests\n\n# Single test\n./gradlew runIntegrationTests -PrubyIntegrationSpecs=specs/dlq_spec.rb\n\n# Manual: set up artifacts first, then run directly from qa/integration/\n./gradlew installIntegrationTestGems  # build Logstash tarball + install test gems\n./gradlew copyEs                      # download Elasticsearch (if spec needs it)\n./gradlew copyFilebeat                # download Filebeat (if spec needs it)\ncd qa/integration\nBUNDLE_PATH=../../build/qa/integration/vendor \\\n  ../../vendor/jruby/bin/jruby -S bundle exec rspec specs/dlq_spec.rb\n```\n\nWhich artifact tasks to run depends on the `services:` list in each spec's fixture YAML.\n### Architecture: Fixture + Service Pattern\n\nEvery integration test has a matching YAML fixture file:\n- Test: `qa/integration/specs/dlq_spec.rb`\n- Fixture: `qa/integration/fixtures/dlq_spec.yml`\n\n**Fixture YAML structure:**\n```yaml\n---\nservices:\n  - logstash\n  - elasticsearch\nconfig: |-\n  input { generator { count => 100 } }\n  output { elasticsearch { index => \"test\" } }\n```\n\nFixtures can also use ERB for dynamic values and define multiple named configs as a hash.\n\n**In test code:**\n```ruby\n@fixture = Fixture.new(__FILE__)           # Loads matching YAML\n@logstash = @fixture.get_service(\"logstash\")\nconfig = @fixture.config(\"root\", { :port => 9200 })  # ERB interpolation\n```\n\n### Key Framework Classes\n\n- **`Fixture`** (`framework/fixture.rb`) — Bootstraps services and loads config from YAML fixtures.\n- **`TestSettings`** (`framework/settings.rb`) — Merges per-test YAML with global `suite.yml`, performs ERB expansion.\n- **`LogstashService`** (`services/logstash_service.rb`) — Manages a Logstash process from the built tarball. Key methods: `start_with_config_string(config)`, `start_background(config_file)`, `wait_for_logstash`, `teardown`.\n- **`MonitoringAPI`** (`services/monitoring_api.rb`) — HTTP wrapper for the Logstash REST API (`pipeline_stats`, `node_info`, `health_report`).\n- **Helpers** (`framework/helpers.rb`) — `wait_for_port`, `is_port_open?`, `send_data`, `config_to_temp_file`, `random_port`.\n\n### Service Management\n\nServices are auto-discovered by naming convention (`*_service.rb` in `services/`). Each service consists of:\n1. A Ruby class extending `Service` (e.g. `ElasticsearchService`)\n2. A `<name>_setup.sh` script (called during test setup)\n3. A `<name>_teardown.sh` script (called during cleanup)\n\nTo add a new service: create all three files in `qa/integration/services/` and reference the service name in your fixture YAML.\n\n### Test File Pattern\n\n```ruby\nrequire_relative '../framework/fixture'\nrequire_relative '../framework/settings'\nrequire_relative '../services/logstash_service'\n\ndescribe \"Feature under test\" do\n  before(:all) {\n    @fixture = Fixture.new(__FILE__)\n    @logstash = @fixture.get_service(\"logstash\")\n  }\n\n  after(:all) { @fixture.teardown }\n  after(:each) { @logstash.teardown }\n\n  it \"should behave correctly\" do\n    @logstash.start_with_config_string(@fixture.config)\n    # ... assertions ...\n  end\nend\n```\n\n### Dependencies\n\nDefined in `qa/integration/integration_tests.gemspec`.\n\n## Acceptance Tests (qa/acceptance/)\n\n### Purpose\n\nVerify that Logstash packages (`.deb`, `.rpm`) install, start, and upgrade correctly on target platforms. These test the artifact, not pipeline behavior.\n\nThese tests assume a Linux environment with `/etc/os-release` and distro package tools; they are not meant to run natively on macOS.\n\n### Running\n\n```bash\n# All acceptance tests (from repo root; uses vendored JRuby)\n./gradlew runAcceptanceTests\n\n# Or install gems once, then run a single test directly\n./gradlew installAcceptanceTestGems\ncd qa\nBUNDLE_PATH=vendor/bundle \\\n  ../vendor/jruby/bin/jruby -S bundle exec rspec acceptance/spec/lib/artifact_operation_spec.rb\n```\n\n### Architecture\n\nTests use RSpec `shared_examples` for reusable test sequences:\n- `\"installable\"` — Download, install, start, stop\n- `\"installable_with_jdk\"` — Install and verify bundled JDK\n- `\"runnable\"` — Verify process is running\n- `\"updated\"` — Test upgrade from previous version\n\nShared examples live in `qa/acceptance/spec/shared_examples/`.\n\n## Docker Tests (qa/docker/)\n\n### Purpose\n\nVerify Docker image build correctness (labels, working directory, architecture), container startup, health checks, and X-Pack features.\n\n### Running\n\nThe tests find images by searching local Docker for `docker.elastic.co/logstash/logstash-{flavor}:{version}-SNAPSHOT`. Build the image first, then run tests. `ARCH` defaults to `x86_64`; set `ARCH=aarch64` when building arm64 images. If you run tests against an image whose architecture differs from the host, set `DOCKER_ARCHITECTURE=amd64|arm64` so the spec helpers compare against the correct target architecture.\n```bash\n# Build images (from repo root)\n./gradlew artifactDocker         # full\n./gradlew artifactDockerOss      # oss\n./gradlew artifactDockerWolfi    # wolfi\n./gradlew artifactDockerIronbank # ironbank\n\n# Run tests through Gradle (build + vendored JRuby)\n./gradlew runAllDockerTests\n./gradlew runDockerFullTests     # or runDockerOssTests / runDockerWolfiTests / runDockerIronbankTests\n\n# Or run directly from qa after installAcceptanceTestGems\ncd qa\nBUNDLE_PATH=vendor/bundle \\\n  ../vendor/jruby/bin/jruby -S bundle exec rspec docker/spec/full\n```\n\n### Architecture\n\nTests use shared examples across image flavors (`full`, `oss`, `wolfi`, `ironbank`). Key helpers in `spec/spec_helper.rb`: `find_image`, `start_container`, `exec_in_container`, `wait_for_logstash`, `cleanup_container`.\n\n## Debugging\n\n```bash\n# Integration: verbose output\ncd qa/integration\nVERBOSE=true BUNDLE_PATH=../../build/qa/integration/vendor \\\n  ../../vendor/jruby/bin/jruby -S bundle exec rspec specs/my_spec.rb -fd\n\n# Docker: inspect container\ndocker run -it logstash-test /bin/bash\n```\n","qa/CLAUDE.md":"See `AGENTS.md`.\n","tools/benchmark-cli/README.md":"### Benchmark CLI\n\n#### Build\n\nTo build a self-contained archive of the benchmark tool simply run:\n\n```bash\ngradle clean assemble\n```\n\nwhich will create the output jar under `build/libs/benchmark-cli.jar`.\n\n#### Running\n\n```bash\n$ java -cp 'benchmark-cli.jar:*' org.logstash.benchmark.cli.Main --help\nOption                           Description\n------                           -----------\n--config <File>                  Path to custom logstash config. Required if\n                                   testcase is set to 'custom'\n--data <File>                    Path to custom logstash data. Only if testcase\n                                   is set to 'custom'\n--distribution-version <String>  The version of a Logstash build to download\n                                   from elastic.co.\n--elasticsearch-export <String>  Optional Elasticsearch host URL to store\n                                   detailed results at. (default: )\n--git-hash <String>              Either a git tree (tag/branch or commit hash),\n                                   optionally prefixed by a Github username,\n                                 if ran against forks.\n                                 E.g.\n                                   'ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7',\n                                 'user-\n                                   name#ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7'\n                                   or 'main'\n--local-path <String>            Path to the root of a local Logstash\n                                   distribution.\n                                  E.g. `/opt/logstash`\n--ls-batch-size <Integer>        Logstash batch size (-b argument) to\n                                   configure. (default: 128)\n--ls-workers <Integer>           Number of Logstash worker threads (-w\n                                   argument) to configure. (default: 2)\n--repeat-data <Integer>          Sets how often the test's dataset should be\n                                   run. (default: 1)\n--testcase <String>              Currently available test cases are 'baseline',\n                                   'apache' and 'custom'. (default: baseline)\n--workdir <File>                 Working directory to store cached files in.\n                                   (default: /Users/d059176/.logstash-\n                                   benchmarks) \n```\n\n##### Example\n\n```bash\n$ java -cp 'benchmark-cli.jar:*' org.logstash.benchmark.cli.Main --workdir=/tmp/benchmark2 --testcase=baseline --distribution-version=5.5.0  \n  ██╗      ██████╗  ██████╗ ███████╗████████╗ █████╗ ███████╗██╗  ██╗          \n  ██║     ██╔═══██╗██╔════╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║  ██║          \n  ██║     ██║   ██║██║  ███╗███████╗   ██║   ███████║███████╗███████║          \n  ██║     ██║   ██║██║   ██║╚════██║   ██║   ██╔══██║╚════██║██╔══██║          \n  ███████╗╚██████╔╝╚██████╔╝███████║   ██║   ██║  ██║███████║██║  ██║          \n  ╚══════╝ ╚═════╝  ╚═════╝ ╚══════╝   ╚═╝   ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝          \n                                                                               \n  ██████╗ ███████╗███╗   ██╗ ██████╗██╗  ██╗███╗   ███╗ █████╗ ██████╗ ██╗  ██╗\n  ██╔══██╗██╔════╝████╗  ██║██╔════╝██║  ██║████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝\n  ██████╔╝█████╗  ██╔██╗ ██║██║     ███████║██╔████╔██║███████║██████╔╝█████╔╝ \n  ██╔══██╗██╔══╝  ██║╚██╗██║██║     ██╔══██║██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗ \n  ██████╔╝███████╗██║ ╚████║╚██████╗██║  ██║██║ ╚═╝ ██║██║  ██║██║  ██║██║  ██╗\n  ╚═════╝ ╚══════╝╚═╝  ╚═══╝ ╚═════╝╚═╝  ╚═╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝\n                                                                               \n  ------------------------------------------\n  Benchmarking Version: 5.5.0\n  Running Test Case: baseline\n  ------------------------------------------\n  Start Time: Sat 7 22 21:28:45.4 2017 CEST\n  Statistical Summary:\n  \n  Elapsed Time: 33s\n  Num Events: 977816\n  Throughput Min: 2000.00\n  Throughput Max: 44500.00\n  Throughput Mean: 37608.31\n  Throughput StdDev: 8985.03\n  Throughput Variance: 80730818.62\n  Mean CPU Usage: 19.27%\n```\n","tools/dependencies-report/README.md":"# Dependency audit tool\n\nThe dependency audit tool automates the verification of the following criteria for all\nthird-party dependencies that are shipped as part of either Logstash core or the [default Logstash \nplugins](https://github.com/elastic/logstash/blob/main/rakelib/plugins-metadata.json):\n* The dependency has been added to the [dependency list file](https://github.com/elastic/logstash/blob/main/tools/dependencies-report/src/main/resources/licenseMapping.csv)\nwith an appropriate project URL and [SPDX license identifier](https://spdx.org/licenses/). \n* The license for the dependency is among those [approved for distribution](https://github.com/elastic/logstash/blob/main/tools/dependencies-report/src/main/resources/acceptableLicenses.csv).\n* There is a corresponding `NOTICE.txt` file in the [notices folder](https://github.com/elastic/logstash/tree/main/tools/dependencies-report/src/main/resources/notices)\ncontaining the appropriate notices or license information for the dependency. These individual \nnotice files will be combined to form the notice file shipped with Logstash.\n\nThe dependency audit tool enumerates all the dependencies, Ruby and Java, direct and transitive,\nfor Logstash core and the default plugins. If any dependencies are found that do not conform to\nthe criteria above, the name of the dependency(ies) along with instructions for resolving are \nprinted to the console and the tool exits with a non-zero return code.\n\nThe dependency audit tool should be run using the script in the `bin` folder:\n\n`$LS_HOME/bin/dependencies-report --csv report.csv`\n","tools/jvm-options-parser/README.md":"Standalone jar for JvmOptionsParser to enable it to run under Java 8 to provide\nhelpful fail fast message when logstash is run older versions of Java.","tools/logstash-docgen/README.md":"**Features**\n\n## Generating the documentation for a Logstash instance\n\nYou can now generate the documentation for a Logstash distribution.\n\n*prerequisite*:\n\n- You need to have installed the plugins inside Logstash, this is necessary to pickup the right file when generating the documentation.\n- You need to have the development gems installed. `bin/logstash-plugin --no-verify --development`\n\nIf you have all of the above you can run this command:\n\n```sh\nrake doc:generate-plugins\nrake doc:generate-plugins[/tmp/the-new-doc] # Depending on the shell you are using you might need to quote the task name, like this rake \"doc:generate-plugins[/tmp/new-doc]\"\n```\n\nThis will give you a output similar to this\n\n```sh\nlogstash-input-file > SUCCESS\nlogstash-input-s3 > SUCCESS\nlogstash-input-kafka > FAIL\n[...]\nExceptions: XXXX\n```\n\nThe generator will try to generate the doc for all the plugins defined in the *Gemfile* and installed in Logstash, if anything goes wrong it won't\nstop the generation of the other plugin. The Task will also report any errors with stacktraces at the end, if one plugin fail the build,\nyou can interrupt the process and it will output the current errors before exiting.\n\n\n## Generating the documentation for all the plugins from the organization\n\nYou can now generate the documentation from main for all the plugin in the *logstash-plugins* organization.\n\n*prerequisite*\n\n- You need to go in the `tools/logstash-docgen` directory\n- You need to have the dependency installed.\n\n To get started you can run the following commands:\n\n ```sh\n cd tools/logstash-docgen\n bundle install\n ```\n\nYou can use the `bin/logstash-docgen` command to generate any plugin that you want, this executable can generate all the plugins or specific one from their main branch.\n\nUsages:\n\n```sh\nbin/logstash-docgen --all # will generate the doc for all the plugins\nbin/logstash-docgen logstash-input-file logstash-input-s3 # generate doc for 2 plugins\n```\n\n**See:** `bin/logstash-docgen --help` for complete usage.\n\n**Notes:**\n- The nature of theses tasks require a lot of external execution to make sure all the doc are done in isolation, this process can take a long time.\n- Some plugins will be skipped, see `logstash-docgen.yml` for details.\n- The script will inject itself as a dependency on the plugin.\n\n\n## Testing the documentation as the plugin author\n\n*prerequisite*\n\n- Declare the `logstash-docgen` as development dependency\n- Add `require \"logstash/docgen/plugin_doc\"` to the `Rakefile`\n- Run `bundle install`\n\nAfter you can have access to a few rake tasks, you can list them with `bundle exec rake -T`\n\n```\nbundle exec rake doc:asciidoc # return the raw asciidoc\nbundle exec rake doc:html # Give you the raw html\n```\n\n## CI test\n\nThe CI can use the `ci/docs.sh` script to correctly bootstrap and execute the docgeneration\n\n\n","tools/paquet/README.md":"# Paquet\n\nThis gem allow a developer to create a uber gem, a uber gem is a gem that content the current gem and his dependencies and is distributed as a tarball.\n\nThis tool allow to define what will be bundler and what should be ignored, it uses the dependencies defined in the gemspec and gemfile to know what to download.\n\nNote that by default no gems will be bundled.\n\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'paquet'\n```\n\nAnd then execute:\n\n    $ bundle\n\n## Usage\nDefine the dependencies in your Rakefile\n\n```ruby\nrequire \"paquet\"\n\nTARGET_DIRECTORY = File.join(File.dirname(__FILE__), \"dependencies\")\n\nPaquet::Task.new(TARGET_DIRECTORY) do\n  pack \"manticore\"\n  pack \"launchy\"\n  pack \"gemoji\"\n  pack \"logstash-output-elasticsearch\"\n\n  # Everything not defined here will be assumed to be provided\n  # by the target installation\n  ignore \"logstash-core-plugin-api\"\n  ignore \"logstash-core\"\nend\n```\n\nAnd run\n\n```\nbundle exec rake paquet:vendor\n```\n\nThe dependencies will be downloaded in your target directory.\n\n## Project Principles\n\n* Community: If a newbie has a bad time, it's a bug.\n* Software: Make it work, then make it right, then make it fast.\n* Technology: If it doesn't do a thing today, we can make it do it tomorrow.\n\n## Contributing\n\nAll contributions are welcome: ideas, patches, documentation, bug reports,\ncomplaints, and even something you drew up on a napkin.\n\nProgramming is not a required skill. Whatever you've seen about open source and\nmaintainers or community members  saying \"send patches or die\" - you will not\nsee that here.\n\nIt is more important to me that you are able to contribute.\n\nFor more information about contributing, see the\n[CONTRIBUTING](../CONTRIBUTING.md) file.\n","x-pack/AGENTS.md":"# x-pack/AGENTS.md\n\nGuidance for coding agents working with X-Pack (Elastic-licensed) features. See also the root `AGENTS.md` for general project conventions.\n\n## Overview\n\nX-Pack provides commercial features that extend the Logstash core. Most features integrate via the **UniversalPlugin** extension pattern; License Checking is a standalone library used by the other features. X-Pack is a separate Ruby package under `x-pack/` with its own test suite. Build without X-Pack by setting `OSS=true`.\n\n## Features\n\n| Feature | Path | Purpose |\n|---------|------|---------|\n| **Monitoring** | `lib/monitoring/` | Collects JVM, system, and pipeline metrics; ships to Elasticsearch |\n| **Config Management** | `lib/config_management/` | Fetches pipeline configs from Elasticsearch/Kibana |\n| **GeoIP Database Management** | `lib/geoip_database_management/` | Auto-downloads and updates GeoIP databases from Elastic CDN |\n| **License Checking** | `lib/license_checker/` | Validates Elasticsearch license for feature gating |\n\n## Extension Pattern\n\nMonitoring, Config Management, and GeoIP follow the same integration pattern. Each has an **extension class** inheriting from `LogStash::UniversalPlugin` that implements two methods:\n\n1. **`additionals_settings(settings)`** — Registers `xpack.*` configuration settings with the core settings registry.\n2. **`register_hooks(hooks)`** — Registers lifecycle callbacks with `LogStash::Runner` (e.g. `before_bootstrap_checks`, `after_bootstrap_checks`).\n\n**Entry point:** `lib/x-pack/logstash_registry.rb` registers all three extensions plus built-in input/output plugins with `LogStash::PLUGIN_REGISTRY`.\n\n**Extension files:**\n- `lib/config_management/extension.rb`\n- `lib/geoip_database_management/extension.rb`\n- `lib/monitoring/monitoring.rb` (extension at bottom of file)\n\n### Adding New Settings\n\nUse `LogStash::Setting::*` classes in `additionals_settings`:\n\n```ruby\ndef additionals_settings(settings)\n  settings.register(LogStash::Setting::BooleanSetting.new(\"xpack.feature.enabled\", false))\n  settings.register(LogStash::Setting::TimeValueSetting.new(\"xpack.feature.interval\", \"5s\"))\n  settings.register(LogStash::Setting::ArrayCoercible.new(\"xpack.feature.hosts\", String, [\"localhost\"]))\n  settings.register(LogStash::Setting::NullableStringSetting.new(\"xpack.feature.password\"))\nend\n```\n\nAll X-Pack settings use the `xpack.` prefix. Elasticsearch connection options (hosts, SSL, auth, proxy) are shared across features via the `ElasticsearchOptions` helper mixin.\n\n## License Checking\n\nFeatures that require a commercial license use the `Licensed` mixin (`lib/license_checker/licensed.rb`):\n\n1. Call `setup_license_checker(FEATURE_NAME)` during initialization.\n2. Wrap feature logic in `with_license_check(raise_on_error) { ... }`.\n3. Override `populate_license_state(xpack_info, is_serverless)` to return `{ :state => :ok | :error, :log_level => ..., :log_message => ... }`.\n\nThe `LicenseManager` (`lib/license_checker/license_manager.rb`) polls Elasticsearch every 30 seconds and notifies observers on state changes. License types: `trial`, `basic`, `standard`, `gold`, `platinum`, `enterprise`. Config management requires trial or above (not basic).\n\n## Running Tests\n\n```bash\n# Unit tests (from repo root)\n./gradlew :logstash-xpack:rubyTests\n\n# Integration tests (requires running Elasticsearch)\n./gradlew :logstash-xpack:rubyIntegrationTests\n\n# Single integration spec\n./gradlew :logstash-xpack:rubyIntegrationTests \\\n  -PrubyIntegrationSpecs=qa/integration/management/multiple_pipelines_spec.rb\n```\n\n### Test Structure\n\n- **Unit specs:** `x-pack/spec/` — Organized by feature (`spec/monitoring/`, `spec/config_management/`, `spec/geoip_database_management/`, `spec/license_checker/`).\n- **Integration tests:** `x-pack/qa/integration/` — Subdirectories for `management/`, `monitoring/`, and `fips-validation/`.\n- **Test helpers:** `x-pack/spec/support/helpers.rb` and `x-pack/spec/support/matchers.rb`.\n- **Test runners:** JUnit-based RSpec invokers in `x-pack/src/test/java/org/logstash/xpack/test/` (`RSpecTests.java` for unit, `RSpecIntegrationTests.java` for integration).\n\n### GeoIP Test Data\n\nThe `unzipGeolite` Gradle task downloads GeoLite2 test databases. Unit tests for GeoIP depend on this task running first (handled automatically by the `rubyTests` dependency chain).\n\n## Key Architectural Patterns\n\n- **Hook-based integration.** Config management intercepts bootstrap checks to swap the local config source with an Elasticsearch source. Monitoring adds an internal pipeline after the agent starts. Neither modifies core code directly.\n- **Singleton managers.** `GeoipDatabaseManagement::Manager` and `LicenseChecker::LicenseManager` are singletons with thread-safe initialization via Mutex.\n- **Observer pattern.** Database subscriptions and license managers notify observers on state changes, enabling features to react dynamically without polling.\n- **Internal pipelines.** Monitoring generates its pipeline config from an ERB template (`lib/template.cfg.erb`) and injects it via `InternalPipelineSource`. These run alongside user pipelines but are hidden from the user-facing API.\n","x-pack/CLAUDE.md":"See `AGENTS.md`.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/elastic/logstash/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md\n\nThis file provides guidance to coding agents when working with code in this repository.\n\n## Project Overview\n\nLogstash is a server-side data processing pipeline that ingests data from multiple sources, transforms it, and sends it to various destinations. It's part of the Elastic Stack and is a **hybrid Java/Ruby application** using JRuby.\n\n### JRuby and versions\n\n- The JRuby version is defined in **`versions.yml`** (repo root). The project uses **vendored JRuby only** — no external Ruby, rvm, or rbenv is required; Gradle installs JRuby into `vendor/jruby` via the bootstrap task.\n- The gem path includes a Ruby compatibility version segment (e.g. `vendor/bundle/jruby/X.Y.0`). It is intentionally hardcoded in Gradle (`rubyUtils.gradle`), shell scripts (`bin/logstash.lib.sh`), and Java test helpers (e.g. `RubyEnvTestCase`). To find the current value, grep for `vendor/bundle/jruby/` in `bin/logstash.lib.sh`. Do not treat it as a bug or \"dynamicize\" it when working on the codebase.\n\n### Single source of truth for versions\n\n- **`versions.yml`** in the repository root is the single source of truth for Logstash version, JRuby version, bundled JDK, Jackson, and related coordinates. Gradle loads it in `settings.gradle`; the `logstash-core` and `logstash-core-plugin-api` gemspecs copy from it. When in doubt about a version, check or update `versions.yml`.\n\n## Build Commands\n\nRun all build and test commands from the **repository root** (where `gradlew` and `versions.yml` live), unless a specific task or doc says otherwise.\n\nAll commands require a JDK that meets the `sourceCompatibility` defined in `build.gradle` (currently 17+). JDK 21 is preferred and is the version bundled for distribution (see `bundled_jdk` in `versions.yml`). Ruby tests run via vendored JRuby — no external Ruby, rvm, or rbenv needed.\n\nUse Gradle tasks as the normal entry points from the repo root. Rake and bundle are used as leaf tools where the existing Gradle tasks or scripts already wire them up.\n\n```bash\n# Initial setup (required once)\n./gradlew bootstrap\n\n# Install development dependencies\n./gradlew installDevelopmentGems\n\n# Install default plugins (80+ plugins)\n./gradlew installDefaultGems\n\n# Build distribution artifact (tar.gz)\n./gradlew assembleTarDistribution\n# Other distribution formats (zip, deb, rpm) are built via rake tasks in rakelib/artifacts.rake\n```\n\n**Build performance (CI):** For faster `installDefaultGems`, use `JARS_SKIP=true`; required jars are already vendored or in JRuby stdlib.\n\n## Running Tests\n\n**Test layout:** Unit specs live under **`spec/`** (root) and **`logstash-core/spec/`**. Integration and acceptance tests live under **`qa/`** (e.g. **`qa/integration`** for integration tests). Use the commands below from the repo root; the single-integration-test example uses a path under `qa/integration`.\n\n**Test task structure:** There are two separate Gradle test tasks: `javaTests` (pure Java unit tests) and `rubyTests` (Ruby specs **and** Java tests that depend on Ruby infrastructure). The top-level `test` task excludes all classes itself and simply delegates to both, so `./gradlew test --tests ...` will silently match nothing. Always apply `--tests` to `javaTests` or `rubyTests` directly.\n\n**`javaTests` vs `rubyTests` split:** Several Java test classes are excluded from `javaTests` and included in `rubyTests` because they load Ruby code at runtime. The exclude/include lists are in `logstash-core/build.gradle`. If `--tests` returns \"No tests found for given includes\", the class is likely in the other task. Examples of Java tests that live in `rubyTests`:\n- `OutputDelegatorTest`, `JavaCodecDelegatorTest` — test delegators that wrap Ruby plugin instances\n- `ConfigCompilerTest`, `CompiledPipelineTest`, `EventConditionTest` — test config compilation which invokes the Ruby parser\n- `PluginFactoryExtTest` — tests Ruby plugin instantiation\n\n```bash\n# All unit tests (Java + Ruby)\n./gradlew test\n\n# All Java tests (pure Java, no Ruby dependency)\n./gradlew :logstash-core:javaTests\n\n# Single Java test class\n./gradlew :logstash-core:javaTests --tests org.logstash.TimestampTest\n\n# Single Java test method\n./gradlew :logstash-core:javaTests --tests org.logstash.TimestampTest.testEpochMillis\n\n# Java tests by pattern\n./gradlew :logstash-core:javaTests --tests \"org.logstash.settings.*\"\n\n# WRONG — will silently run nothing because `test` excludes all classes:\n# ./gradlew :logstash-core:test --tests org.logstash.TimestampTest\n\n# Java test that depends on Ruby — must use rubyTests, not javaTests:\n./gradlew :logstash-core:rubyTests --tests org.logstash.config.ir.compiler.OutputDelegatorTest\n\n# All Ruby tests (core) — also runs the Ruby-dependent Java tests listed above\n./gradlew :logstash-core:rubyTests\n\n# Single Ruby spec file\nSPEC_OPTS=\"-fd -P logstash-core/spec/logstash/agent_spec.rb\" \\\n  ./gradlew :logstash-core:rubyTests --tests org.logstash.RSpecTests\n\n# All integration tests\n./gradlew runIntegrationTests\n\n# Single integration test\n./gradlew runIntegrationTests -PrubyIntegrationSpecs=specs/slowlog_spec.rb\n\n# All X-Pack Ruby tests\n./gradlew :logstash-xpack:rubyTests\n\n# Single X-Pack Ruby test\nSPEC_OPTS=\"-fd -P spec/config_management/elasticsearch_source_spec.rb\" \\\n  ./gradlew :logstash-xpack:rubyTests --rerun-tasks\n\n# Single Ruby test for core or X-Pack (fast, direct JRuby)\nbin/rspec logstash-core/spec/logstash/some_spec.rb\nbin/rspec x-pack/spec/config_management/elasticsearch_source_spec.rb\n```\n\n## Quick Verification\n\n```bash\n# Start Logstash with inline config\nbin/logstash -e 'input { stdin { } } output { stdout {} }'\n```\n\n## Architecture\n\n### Dual Java/Ruby Runtime\n- **Java Core** (`logstash-core/src/main/java/org/logstash/`): Pipeline execution engine, event processing, persistent queue, configuration compilation\n- **Ruby Core** (`logstash-core/lib/logstash/`): Agent lifecycle, plugin coordination, REST API, configuration loading, metric store, settings management\n\n### Key Components\n- **Agent** (`logstash-core/lib/logstash/agent.rb`): Main orchestrator, manages pipeline lifecycle\n- **JavaPipeline** (`logstash-core/lib/logstash/java_pipeline.rb`): Ruby class for core event processing with worker threads, backed by Java execution infrastructure in `logstash-core/src/main/java/org/logstash/execution/`\n- **Plugin System** (`logstash-core/lib/logstash/plugins/`): Input/Filter/Output/Codec plugin infrastructure\n- **Plugin API** (`logstash-core-plugin-api/`): Public interface for plugin developers\n- **JvmOptionsParser** (`tools/jvm-options-parser/`): Standalone Java 11 subproject that parses `config/jvm.options` and composes JVM command-line flags before JRuby starts. Supports version-conditional syntax (e.g. `11-:`, `16-20:`), resolves `${ENV_VAR}` references, and enforces mandatory JVM flags. Invoked from `bin/logstash.lib.sh` during `setup_java()`.\n\n### Directory Structure\n- `logstash-core/` - Core framework (Java + Ruby)\n- `lib/pluginmanager/` - Plugin installation and management\n- `x-pack/` - Elastic-licensed features (disable with `OSS=true`)\n- `qa/` - Quality assurance: acceptance, integration, Docker tests\n- `tools/` - Development tools (docgen, benchmarks, release automation)\n\n### Gradle–Ruby interaction\n\n- **`rubyUtils.gradle`** drives Ruby from Gradle: it uses JRuby's `ScriptingContainer`, `executeJruby`, and invokes bundle/rake. The vendored JRuby lives under **`vendor/jruby`** after `./gradlew bootstrap`. When debugging Gradle tasks that run Ruby (e.g. plugin install, Rake), this is where the wiring lives.\n\n## Code Design Practices\n\nWhen modifying code, don't just append to what's there — evaluate whether the surrounding code still makes sense with the change. If a method, class, or abstraction was designed for one case and you're adding a second, consider restructuring so the result reads as if both cases were planned from the start. Each distinct concept should have its own clearly-named unit, and higher-level code should compose those units so the intent is obvious without reading the implementation.\n\n### Plugin System Conventions\n\n- **Inherit from the type-specific base class.** Each plugin type has a base in `logstash-core/lib/logstash/{type}s/base.rb` (inputs, filters, outputs, codecs). Override `register` for setup plus the type-specific entry point: `run` (inputs), `filter` (filters), `receive`/`multi_receive` (outputs), `decode`/`encode` (codecs).\n- **Declare configuration with the `config` DSL**, not raw `@params` access. Use `config :name, :validate => :type, :default => value` — defined in `logstash-core/lib/logstash/config/mixin.rb`. The framework handles validation, coercion, and documentation generation from these declarations.\n- **Set output concurrency at the class level** with `concurrency :shared` or `concurrency :single` in `logstash-core/lib/logstash/outputs/base.rb`. This determines which `OutputStrategy` the pipeline uses to wrap the plugin: `:shared` means multiple worker threads can call the plugin simultaneously; `:single` serialises all calls so only one thread executes the plugin at a time. Most new outputs should use `:shared`.\n- **Plugin lookup goes through `LogStash::Plugins::Registry#lookup`** (`logstash-core/lib/logstash/plugins/registry.rb`). New built-in plugins register in `logstash-core/lib/logstash/plugins/builtin.rb`. External plugins are discovered as gems.\n\n### Delegator and Metrics Patterns\n\n- **Every plugin runs behind a delegator** that adds metrics collection and thread-safety. Filters: `FilterDelegatorExt` (Java), outputs: `OutputDelegatorExt` (Java), codecs: `Codecs::Delegator` (`logstash-core/lib/logstash/codecs/delegator.rb`). When changing plugin behavior, determine whether the change belongs in the delegator (cross-cutting concern) or in the plugin itself.\n- **Metrics follow a namespace hierarchy.** Use `metric.namespace(:sub)` to create children. `NullMetric` implements the same interface but discards data — it's the null-object stand-in when `enable_metric => false` or during testing. Never instantiate metric objects directly; accept them from the framework.\n- **Standard mixins are included automatically.** All plugins get `LogStash::Util::Loggable` (provides `logger`, `slow_logger`, `deprecation_logger`) and `LogStash::Config::Mixin` (config DSL). Additional mixins like `ECSCompatibilitySupport` and `EventFactorySupport` are opt-in per plugin.\n\n### Pipeline Execution Model\n\n- **The worker loop is the hot path.** `WorkerLoop` (`logstash-core/src/main/java/org/logstash/execution/WorkerLoop.java`) pulls batches from the queue, runs them through the compiled pipeline, and signals completion. Shutdown is coordinated via `AtomicBoolean` flags — do not add blocking calls or heavy allocations inside this loop.\n- **Pipeline lifecycle follows a strict ordering** in `JavaPipeline` (`logstash-core/lib/logstash/java_pipeline.rb`): outputs and filters register first, then workers start, then inputs start last. On shutdown the reverse applies: inputs stop first, workers drain, then filters and outputs close. Violating this ordering causes data loss or deadlocks.\n- **Use `Concurrent::AtomicBoolean` (Ruby) or `java.util.concurrent.atomic.AtomicBoolean` (Java) for cross-thread signaling.** Do not use plain boolean instance variables for state shared across threads — Ruby's GIL does not guarantee visibility, and JRuby runs without one.\n\n#### Runtime Code Generation (Config → Java Classes)\n\nLogstash configs are compiled into executable Java classes at runtime through a multi-stage pipeline:\n\n1. **Parse:** Treetop grammar (`logstash-core/lib/logstash/compiler/lscl/lscl_grammar.treetop`) parses `.conf` files into an AST.\n2. **IR:** `ConfigCompiler` (`logstash-core/src/main/java/org/logstash/config/ir/ConfigCompiler.java`) converts the AST into a `PipelineIR` — a directed acyclic graph of plugins and conditionals.\n3. **Compile:** `DatasetCompiler` (`logstash-core/src/main/java/org/logstash/config/ir/compiler/DatasetCompiler.java`) generates Java source strings for each pipeline vertex. `ComputeStepSyntaxElement` (`logstash-core/src/main/java/org/logstash/config/ir/compiler/ComputeStepSyntaxElement.java`) compiles these sources into classes via Janino at runtime. Generated classes live under the `org.logstash.generated` package.\n4. **Execute:** `CompiledPipeline` (`logstash-core/src/main/java/org/logstash/execution/CompiledPipeline.java`) wires the compiled datasets into the worker loop.\n\nConditionals from config (`if [field] == \"value\"`) compile into `EventCondition` lambdas wired into `SplitDataset` nodes. To inspect generated source code, add these flags to `config/jvm.options`:\n```\n-Dorg.codehaus.janino.source_debugging.enable=true\n-Dorg.codehaus.janino.source_debugging.dir=/path/to/output\n```\n\n### Java Code Conventions\n\n- **Packages are organized by functional domain**, not architecture layers. Main packages under `org.logstash`: `execution` (pipeline/workers), `config.ir` (config compilation), `plugins` (plugin infrastructure), `ackedqueue` (persistent queue), `instrument.metrics` (metrics), `ext` (JRuby extensions), `settings` (configuration types), `log` (logging). Place new classes in the package matching their domain.\n- **Naming conventions:** JRuby extension classes use the `Ext` suffix (`LoggableExt`, `AbstractSimpleMetricExt`). Delegator classes use `Delegator` suffix. Abstract bases use the `Abstract` prefix. Most concrete classes are `public final class` — use plain `public class` only when subclassing is intended (e.g. `BaseSetting<T>`).\n- **Prefer unchecked exceptions.** Custom exceptions extend `RuntimeException` (e.g. `FieldReference.IllegalSyntaxException`, `Event.InvalidTagsTypeException`). Checked exceptions appear only when unavoidable (`IOException`, `InterruptedException`). Include descriptive messages and preserve exception chains via the `cause` parameter.\n- **Null handling:** Use `Objects.requireNonNull()` for constructor parameter validation. Use `@Nullable` annotation when null is an intentional part of the contract. Use `Optional` for parse/lookup chains that may fail (see `Timestamp.tryParse`), not as a general null replacement. Prefer explicit null checks before dereference over implicit assumptions.\n- **Logging uses Log4j 2.** Declare loggers as `private static final Logger LOGGER = LogManager.getLogger(MyClass.class)`. Use `TRACE` for diagnostic detail (parse attempts, fallback paths), `WARN` for recoverable issues, `ERROR` for uncaught exceptions, `FATAL` for unrecoverable system errors. Include context (thread name, field type, value) in messages.\n- **Thread safety:** Prefer immutable `final` fields as the primary safety mechanism. Use `ConcurrentHashMap` for thread-safe caches (see `FieldReference.CACHE`). Use `AtomicBoolean` for shutdown signaling. Document thread-safety guarantees in class-level JavaDoc when the class is shared across threads.\n\n### REST API Conventions\n\n- **API modules inherit from `LogStash::Api::Modules::Base`** (`logstash-core/lib/logstash/api/modules/base.rb`), which extends `Sinatra::Base`. Each module receives the `agent` reference and a `CommandFactory` in its constructor. Define routes using Sinatra's `get`/`put` DSL inside the module class.\n- **Routing uses rack namespace mapping.** Endpoints are mounted at path prefixes (`/_node`, `/_stats`, `/_node/stats`, `/_node/plugins`, `/_node/logging`, `/_health_report`) defined in `rack_namespaces` in `logstash-core/lib/logstash/api/rack_app.rb`. Add new endpoint namespaces there, not via ad-hoc Sinatra routing.\n- **All responses go through `respond_with`** (`logstash-core/lib/logstash/api/app_helpers.rb`). It automatically wraps JSON responses with default metadata (host, version, http_address, id) unless `exclude_default_metadata` is passed. Use `respond_with(data)` — do not write raw JSON responses.\n- **Errors use `ApiError` subclasses** (`logstash-core/lib/logstash/api/errors.rb`): `NotFoundError` (404), `BadRequest` (400), `RequestTimeout` (408). Raise these from route handlers; the base module catches them and formats a consistent error envelope with `path`, `status`, and `error` fields.\n- **Business logic lives in command classes**, not in route handlers. Commands inherit from `LogStash::Api::Commands::Base` (`logstash-core/lib/logstash/api/commands/base.rb`) and access metrics via `service.extract_metrics(path, *keys)`. Route handlers should only call `factory.build(:command_name)` and pass results to `respond_with`.\n\n### Java/Ruby Boundary\n\n- **JRuby extensions use `@JRubyClass`/`@JRubyMethod` annotations.** Classes register in `RubyUtil` (`logstash-core/src/main/java/org/logstash/RubyUtil.java`) via `setupLogstashClass` or `defineClassUnder`. Follow existing patterns in `RubyUtil`'s static initializer when adding new Java-backed Ruby classes.\n- **`ThreadContext` must be obtained at point of use** via `RubyUtil.RUBY.getCurrentContext()`, never stored at construction time. Storing it causes `NullPointerException` when used from a different thread (JRuby 10+). See `RubyCodecDelegator` for the correct pattern.\n- **Test the round-trip through Java.** Integration tests should exercise the full path from Ruby through the Java layer and back, not just the Ruby classes in isolation. This catches type coercion issues and metric namespace registration bugs that unit tests miss.\n- **Java tests that load Ruby:** Some Java tests (e.g. `RubyEnvTestCase` and subclasses) set **GEM_HOME** / **GEM_PATH** to the vendored bundle path (`vendor/bundle/jruby/<ruby-compat-version>`) so they can load Logstash Ruby code. Check `bin/logstash.lib.sh` or `rubyUtils.gradle` for the current version segment. That path is intentional; do not change it when touching those tests.\n\n### Test Practices\n\n- **Use `sample_one` helper for filter pipeline tests.** `PipelineHelpers#sample_one` (`logstash-core/spec/support/pipeline/pipeline_helpers.rb`) spins up a real pipeline, feeds events through a filter config, and captures results — preferred over manually constructing pipeline objects.\n- **Create dummy plugin classes inside spec files for isolation.** Define throwaway classes inheriting from the appropriate base, register with `LogStash::PLUGIN_REGISTRY.add`. See `pipeline_helpers.rb` for examples.\n- **Use `shared_examples` for interface contracts and `shared_context` for common setup.** Examples: `\"metrics commons operations\"` in `logstash-core/spec/support/shared_examples.rb`, `\"execution_context\"` in `logstash-core/spec/support/shared_contexts.rb`.\n- **Integration tests use the Fixture + Service pattern.** `Fixture` (`qa/integration/framework/fixture.rb`) bootstraps services and loads config from YAML fixture files under `qa/integration/fixtures/`.\n- **Test that existing rescue blocks catch new code paths.** When a method has a `rescue => e` that returns `nil`, verify that exceptions raised in newly added branches are caught by it. It is easy to accidentally add code outside the `begin...rescue` scope.\n- **Clean up JVM state in `after` blocks.** Any `java.lang.System.setProperty` call in a test must have a corresponding `clearProperty` in `after` to avoid leaking state to other specs.\n- **Prefer concrete test doubles over mocks.** The codebase uses purpose-built classes (`DummyInput`, `DummyFilter`, `DummyOutput` in `logstash-core/spec/support/mocks_classes.rb`) that inherit from real base classes with minimal implementations. Use these over RSpec `double()` when testing pipeline behavior — they exercise real plugin lifecycle methods.\n- **Reserve mocks for infrastructure boundaries.** `LogStash::Agent`, loggers, and `LogStash::SETTINGS` are commonly stubbed to prevent real pipeline startup. Use `allow(obj).to receive(:method).and_call_original` when you need the mock for setup but want to test real behavior. The codebase uses plain `double()` and `.as_null_object` — not `instance_double` or `class_double` — due to JRuby's dynamic method dispatch.\n\n### Test Validity\n\n- **Verify every new test fails without the corresponding change.** Before treating a test as done, confirm it fails against the pre-change code. A test that passes with or without the fix proves nothing. This is the most basic check that a test is load-bearing.\n- **Assert on the specific behaviour the change introduces, not on pre-existing conditions.** If the system already returned `200 OK` before your fix, asserting `200 OK` does not validate the fix. Pin the assertion to an observable difference — a new field, a changed value, a previously-missing log line.\n- **For bug fixes, reproduce the bug in the test first.** Write the test, watch it fail for the same reason the bug manifests, then apply the fix and watch it pass. If you cannot make the test fail, either the bug is not what you think or the test is not targeting it.\n- **Beware of tautological mocks.** Stubbing a method to return the value you then assert is a circular proof. Mocks should simulate collaborators, not replace the code under test. If removing the implementation and keeping only the mock still passes the test, the test is testing the mock.\n\n## Plugin Development\n\nPlugins live in separate repositories under [logstash-plugins](https://github.com/logstash-plugins). Each plugin is a self-contained Ruby gem. Plugin types: input, filter, output, codec.\n\n- The **`logstash-core-plugin-api`** version is in **`versions.yml`**; plugins (in separate repos) depend on it.\n- Templates for new plugins are in **`lib/pluginmanager/templates/`**.\n- Supported plugin versions and compatibility are tracked in the [logstash-plugins `.ci`](https://github.com/logstash-plugins/.ci) repo (e.g. `logstash-versions.yml`).\n\n## Sensitive Data Handling\n\nWhen adding configuration parameters for sensitive data:\n- In Ruby: Apply `:password` validation or wrap with `Logstash::Util::Password`\n- In Java: See `Password.java` and `SecretVariable.java` for patterns\n- Override `toString()` to mask sensitive values\n- Never log secrets at any log level by default\n\nExample:\n```ruby\n:my_auth => { :validate => :password }\n# Or wrap manually:\n::LogStash::Util::Password.new(my_auth)\n```\n\n## Environment Variables\n\n```bash\nexport LOGSTASH_SOURCE=1\nexport LOGSTASH_PATH=/path/to/logstash\nexport JAVA_HOME=/path/to/jdk  # JDK 17+ (see build.gradle sourceCompatibility)\nexport OSS=true                 # Build without X-Pack\n```\n\n## Debugging\n\n```bash\n# Attach debugger to running Logstash\nLS_JAVA_OPTS=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005\" bin/logstash ...\n```\n\n## Breaking Changes Policy\n\n1. Implement new behavior as opt-in (MINOR)\n2. Add deprecation warnings (MINOR)\n3. Change default, keep opt-out option (MAJOR)\n4. Remove old behavior (MAJOR)\n\n## Documentation\n\n### Directory Structure\n- `docs/reference/` - Main user-facing documentation (Markdown)\n- `docs/extend/` - Plugin development and contribution guides (Markdown)\n- `docs/release-notes/` - Version-specific release notes, breaking changes, deprecations\n- `docs/static/` - Static assets (images, OpenAPI specs)\n- `tools/logstash-docgen/` - Auto-generates plugin documentation from code\n\n### Formats\n- **Reference docs**: Markdown (`.md`) with front matter and admonition syntax\n- **Plugin docs**: AsciiDoc (`.asciidoc`) with dynamic ID variables\n\n### Markdown Conventions (Reference Docs)\n```markdown\n---\nmapped_pages:\n  - https://www.elastic.co/guide/en/logstash/current/page.html\n---\n\n# Heading Title [anchor-id]\n\n::::{admonition} Note title\n:class: note\nContent here\n::::\n```\n\n### AsciiDoc Conventions (Plugin Docs)\n```asciidoc\n:plugin: example\n:type: filter\n\n[id=\"plugins-{type}s-{plugin}\"]\n=== Example filter plugin\n\n[id=\"plugins-{type}s-{plugin}-options\"]\n==== Configuration Options\n```\n\nUse dynamic IDs (`{type}`, `{plugin}`) to prevent ID duplication across plugin versions.\n\n### Guidelines\n- All PRs must include documentation updates when applicable\n- Plugin documentation is required before merging\n- Follow Elastic documentation standards: one sentence per line in AsciiDoc, use `[id=\"...\"]` anchors (not Markdown-style), prefer `[source,lang]` for code blocks, use `<<anchor>>` for cross-references, and keep titles in title case\n- Images go in `docs/reference/images/` (PNG format)\n\n## See also\n\n- **CONTRIBUTING.md** — Contribution process, sensitive data, and where to ask questions\n- **STYLE.md** — Ruby style (indentation, logging, hash syntax) and consistency\n","isInternal":false,"tokens":5622,"sizeBytes":24429},{"name":"AGENTS.md","path":"qa/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/elastic/logstash/HEAD/qa/AGENTS.md","title":"qa - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# qa/AGENTS.md\n\nGuidance for coding agents working with the QA test infrastructure. See also the root `AGENTS.md` for general project conventions.\n\n## Overview\n\nThe `qa/` directory contains three independent test suites:\n\n| Suite | Path | Purpose |\n|-------|------|---------|\n| **Integration** | `qa/integration/` | End-to-end tests with live services (Elasticsearch, Kafka, etc.) |\n| **Acceptance** | `qa/acceptance/` | Package artifact testing (deb, rpm install/upgrade) |\n| **Docker** | `qa/docker/` | Docker image and container verification |\n\nEach suite has its own dependencies and execution workflow.\n\n## Integration Tests (qa/integration/)\n\n### Running\n\n```bash\n# All integration tests via Gradle (handles all setup automatically)\n./gradlew runIntegrationTests\n\n# Single test\n./gradlew runIntegrationTests -PrubyIntegrationSpecs=specs/dlq_spec.rb\n\n# Manual: set up artifacts first, then run directly from qa/integration/\n./gradlew installIntegrationTestGems  # build Logstash tarball + install test gems\n./gradlew copyEs                      # download Elasticsearch (if spec needs it)\n./gradlew copyFilebeat                # download Filebeat (if spec needs it)\ncd qa/integration\nBUNDLE_PATH=../../build/qa/integration/vendor \\\n  ../../vendor/jruby/bin/jruby -S bundle exec rspec specs/dlq_spec.rb\n```\n\nWhich artifact tasks to run depends on the `services:` list in each spec's fixture YAML.\n### Architecture: Fixture + Service Pattern\n\nEvery integration test has a matching YAML fixture file:\n- Test: `qa/integration/specs/dlq_spec.rb`\n- Fixture: `qa/integration/fixtures/dlq_spec.yml`\n\n**Fixture YAML structure:**\n```yaml\n---\nservices:\n  - logstash\n  - elasticsearch\nconfig: |-\n  input { generator { count => 100 } }\n  output { elasticsearch { index => \"test\" } }\n```\n\nFixtures can also use ERB for dynamic values and define multiple named configs as a hash.\n\n**In test code:**\n```ruby\n@fixture = Fixture.new(__FILE__)           # Loads matching YAML\n@logstash = @fixture.get_service(\"logstash\")\nconfig = @fixture.config(\"root\", { :port => 9200 })  # ERB interpolation\n```\n\n### Key Framework Classes\n\n- **`Fixture`** (`framework/fixture.rb`) — Bootstraps services and loads config from YAML fixtures.\n- **`TestSettings`** (`framework/settings.rb`) — Merges per-test YAML with global `suite.yml`, performs ERB expansion.\n- **`LogstashService`** (`services/logstash_service.rb`) — Manages a Logstash process from the built tarball. Key methods: `start_with_config_string(config)`, `start_background(config_file)`, `wait_for_logstash`, `teardown`.\n- **`MonitoringAPI`** (`services/monitoring_api.rb`) — HTTP wrapper for the Logstash REST API (`pipeline_stats`, `node_info`, `health_report`).\n- **Helpers** (`framework/helpers.rb`) — `wait_for_port`, `is_port_open?`, `send_data`, `config_to_temp_file`, `random_port`.\n\n### Service Management\n\nServices are auto-discovered by naming convention (`*_service.rb` in `services/`). Each service consists of:\n1. A Ruby class extending `Service` (e.g. `ElasticsearchService`)\n2. A `<name>_setup.sh` script (called during test setup)\n3. A `<name>_teardown.sh` script (called during cleanup)\n\nTo add a new service: create all three files in `qa/integration/services/` and reference the service name in your fixture YAML.\n\n### Test File Pattern\n\n```ruby\nrequire_relative '../framework/fixture'\nrequire_relative '../framework/settings'\nrequire_relative '../services/logstash_service'\n\ndescribe \"Feature under test\" do\n  before(:all) {\n    @fixture = Fixture.new(__FILE__)\n    @logstash = @fixture.get_service(\"logstash\")\n  }\n\n  after(:all) { @fixture.teardown }\n  after(:each) { @logstash.teardown }\n\n  it \"should behave correctly\" do\n    @logstash.start_with_config_string(@fixture.config)\n    # ... assertions ...\n  end\nend\n```\n\n### Dependencies\n\nDefined in `qa/integration/integration_tests.gemspec`.\n\n## Acceptance Tests (qa/acceptance/)\n\n### Purpose\n\nVerify that Logstash packages (`.deb`, `.rpm`) install, start, and upgrade correctly on target platforms. These test the artifact, not pipeline behavior.\n\nThese tests assume a Linux environment with `/etc/os-release` and distro package tools; they are not meant to run natively on macOS.\n\n### Running\n\n```bash\n# All acceptance tests (from repo root; uses vendored JRuby)\n./gradlew runAcceptanceTests\n\n# Or install gems once, then run a single test directly\n./gradlew installAcceptanceTestGems\ncd qa\nBUNDLE_PATH=vendor/bundle \\\n  ../vendor/jruby/bin/jruby -S bundle exec rspec acceptance/spec/lib/artifact_operation_spec.rb\n```\n\n### Architecture\n\nTests use RSpec `shared_examples` for reusable test sequences:\n- `\"installable\"` — Download, install, start, stop\n- `\"installable_with_jdk\"` — Install and verify bundled JDK\n- `\"runnable\"` — Verify process is running\n- `\"updated\"` — Test upgrade from previous version\n\nShared examples live in `qa/acceptance/spec/shared_examples/`.\n\n## Docker Tests (qa/docker/)\n\n### Purpose\n\nVerify Docker image build correctness (labels, working directory, architecture), container startup, health checks, and X-Pack features.\n\n### Running\n\nThe tests find images by searching local Docker for `docker.elastic.co/logstash/logstash-{flavor}:{version}-SNAPSHOT`. Build the image first, then run tests. `ARCH` defaults to `x86_64`; set `ARCH=aarch64` when building arm64 images. If you run tests against an image whose architecture differs from the host, set `DOCKER_ARCHITECTURE=amd64|arm64` so the spec helpers compare against the correct target architecture.\n```bash\n# Build images (from repo root)\n./gradlew artifactDocker         # full\n./gradlew artifactDockerOss      # oss\n./gradlew artifactDockerWolfi    # wolfi\n./gradlew artifactDockerIronbank # ironbank\n\n# Run tests through Gradle (build + vendored JRuby)\n./gradlew runAllDockerTests\n./gradlew runDockerFullTests     # or runDockerOssTests / runDockerWolfiTests / runDockerIronbankTests\n\n# Or run directly from qa after installAcceptanceTestGems\ncd qa\nBUNDLE_PATH=vendor/bundle \\\n  ../vendor/jruby/bin/jruby -S bundle exec rspec docker/spec/full\n```\n\n### Architecture\n\nTests use shared examples across image flavors (`full`, `oss`, `wolfi`, `ironbank`). Key helpers in `spec/spec_helper.rb`: `find_image`, `start_container`, `exec_in_container`, `wait_for_logstash`, `cleanup_container`.\n\n## Debugging\n\n```bash\n# Integration: verbose output\ncd qa/integration\nVERBOSE=true BUNDLE_PATH=../../build/qa/integration/vendor \\\n  ../../vendor/jruby/bin/jruby -S bundle exec rspec specs/my_spec.rb -fd\n\n# Docker: inspect container\ndocker run -it logstash-test /bin/bash\n```\n","isInternal":false,"tokens":1607,"sizeBytes":6575},{"name":"AGENTS.md","path":"x-pack/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/elastic/logstash/HEAD/x-pack/AGENTS.md","title":"x-pack - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# x-pack/AGENTS.md\n\nGuidance for coding agents working with X-Pack (Elastic-licensed) features. See also the root `AGENTS.md` for general project conventions.\n\n## Overview\n\nX-Pack provides commercial features that extend the Logstash core. Most features integrate via the **UniversalPlugin** extension pattern; License Checking is a standalone library used by the other features. X-Pack is a separate Ruby package under `x-pack/` with its own test suite. Build without X-Pack by setting `OSS=true`.\n\n## Features\n\n| Feature | Path | Purpose |\n|---------|------|---------|\n| **Monitoring** | `lib/monitoring/` | Collects JVM, system, and pipeline metrics; ships to Elasticsearch |\n| **Config Management** | `lib/config_management/` | Fetches pipeline configs from Elasticsearch/Kibana |\n| **GeoIP Database Management** | `lib/geoip_database_management/` | Auto-downloads and updates GeoIP databases from Elastic CDN |\n| **License Checking** | `lib/license_checker/` | Validates Elasticsearch license for feature gating |\n\n## Extension Pattern\n\nMonitoring, Config Management, and GeoIP follow the same integration pattern. Each has an **extension class** inheriting from `LogStash::UniversalPlugin` that implements two methods:\n\n1. **`additionals_settings(settings)`** — Registers `xpack.*` configuration settings with the core settings registry.\n2. **`register_hooks(hooks)`** — Registers lifecycle callbacks with `LogStash::Runner` (e.g. `before_bootstrap_checks`, `after_bootstrap_checks`).\n\n**Entry point:** `lib/x-pack/logstash_registry.rb` registers all three extensions plus built-in input/output plugins with `LogStash::PLUGIN_REGISTRY`.\n\n**Extension files:**\n- `lib/config_management/extension.rb`\n- `lib/geoip_database_management/extension.rb`\n- `lib/monitoring/monitoring.rb` (extension at bottom of file)\n\n### Adding New Settings\n\nUse `LogStash::Setting::*` classes in `additionals_settings`:\n\n```ruby\ndef additionals_settings(settings)\n  settings.register(LogStash::Setting::BooleanSetting.new(\"xpack.feature.enabled\", false))\n  settings.register(LogStash::Setting::TimeValueSetting.new(\"xpack.feature.interval\", \"5s\"))\n  settings.register(LogStash::Setting::ArrayCoercible.new(\"xpack.feature.hosts\", String, [\"localhost\"]))\n  settings.register(LogStash::Setting::NullableStringSetting.new(\"xpack.feature.password\"))\nend\n```\n\nAll X-Pack settings use the `xpack.` prefix. Elasticsearch connection options (hosts, SSL, auth, proxy) are shared across features via the `ElasticsearchOptions` helper mixin.\n\n## License Checking\n\nFeatures that require a commercial license use the `Licensed` mixin (`lib/license_checker/licensed.rb`):\n\n1. Call `setup_license_checker(FEATURE_NAME)` during initialization.\n2. Wrap feature logic in `with_license_check(raise_on_error) { ... }`.\n3. Override `populate_license_state(xpack_info, is_serverless)` to return `{ :state => :ok | :error, :log_level => ..., :log_message => ... }`.\n\nThe `LicenseManager` (`lib/license_checker/license_manager.rb`) polls Elasticsearch every 30 seconds and notifies observers on state changes. License types: `trial`, `basic`, `standard`, `gold`, `platinum`, `enterprise`. Config management requires trial or above (not basic).\n\n## Running Tests\n\n```bash\n# Unit tests (from repo root)\n./gradlew :logstash-xpack:rubyTests\n\n# Integration tests (requires running Elasticsearch)\n./gradlew :logstash-xpack:rubyIntegrationTests\n\n# Single integration spec\n./gradlew :logstash-xpack:rubyIntegrationTests \\\n  -PrubyIntegrationSpecs=qa/integration/management/multiple_pipelines_spec.rb\n```\n\n### Test Structure\n\n- **Unit specs:** `x-pack/spec/` — Organized by feature (`spec/monitoring/`, `spec/config_management/`, `spec/geoip_database_management/`, `spec/license_checker/`).\n- **Integration tests:** `x-pack/qa/integration/` — Subdirectories for `management/`, `monitoring/`, and `fips-validation/`.\n- **Test helpers:** `x-pack/spec/support/helpers.rb` and `x-pack/spec/support/matchers.rb`.\n- **Test runners:** JUnit-based RSpec invokers in `x-pack/src/test/java/org/logstash/xpack/test/` (`RSpecTests.java` for unit, `RSpecIntegrationTests.java` for integration).\n\n### GeoIP Test Data\n\nThe `unzipGeolite` Gradle task downloads GeoLite2 test databases. Unit tests for GeoIP depend on this task running first (handled automatically by the `rubyTests` dependency chain).\n\n## Key Architectural Patterns\n\n- **Hook-based integration.** Config management intercepts bootstrap checks to swap the local config source with an Elasticsearch source. Monitoring adds an internal pipeline after the agent starts. Neither modifies core code directly.\n- **Singleton managers.** `GeoipDatabaseManagement::Manager` and `LicenseChecker::LicenseManager` are singletons with thread-safe initialization via Mutex.\n- **Observer pattern.** Database subscriptions and license managers notify observers on state changes, enabling features to react dynamically without polling.\n- **Internal pipelines.** Monitoring generates its pipeline config from an ERB template (`lib/template.cfg.erb`) and injects it via `InternalPipelineSource`. These run alongside user pipelines but are hidden from the user-facing API.\n","isInternal":false,"tokens":1158,"sizeBytes":5159},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/elastic/logstash/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"See `AGENTS.md`.\n","isInternal":false,"tokens":6,"sizeBytes":17},{"name":"CLAUDE.md","path":"qa/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/elastic/logstash/HEAD/qa/CLAUDE.md","title":"qa - Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"See `AGENTS.md`.\n","isInternal":false,"tokens":6,"sizeBytes":17},{"name":"CLAUDE.md","path":"x-pack/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/elastic/logstash/HEAD/x-pack/CLAUDE.md","title":"x-pack - Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"See `AGENTS.md`.\n","isInternal":false,"tokens":6,"sizeBytes":17},{"name":"README.md","path":"tools/benchmark-cli/README.md","rawUrl":"https://raw.githubusercontent.com/elastic/logstash/HEAD/tools/benchmark-cli/README.md","title":"benchmark-cli Documentation","category":"plugin-manifest","format":"markdown","content":"### Benchmark CLI\n\n#### Build\n\nTo build a self-contained archive of the benchmark tool simply run:\n\n```bash\ngradle clean assemble\n```\n\nwhich will create the output jar under `build/libs/benchmark-cli.jar`.\n\n#### Running\n\n```bash\n$ java -cp 'benchmark-cli.jar:*' org.logstash.benchmark.cli.Main --help\nOption                           Description\n------                           -----------\n--config <File>                  Path to custom logstash config. Required if\n                                   testcase is set to 'custom'\n--data <File>                    Path to custom logstash data. Only if testcase\n                                   is set to 'custom'\n--distribution-version <String>  The version of a Logstash build to download\n                                   from elastic.co.\n--elasticsearch-export <String>  Optional Elasticsearch host URL to store\n                                   detailed results at. (default: )\n--git-hash <String>              Either a git tree (tag/branch or commit hash),\n                                   optionally prefixed by a Github username,\n                                 if ran against forks.\n                                 E.g.\n                                   'ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7',\n                                 'user-\n                                   name#ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7'\n                                   or 'main'\n--local-path <String>            Path to the root of a local Logstash\n                                   distribution.\n                                  E.g. `/opt/logstash`\n--ls-batch-size <Integer>        Logstash batch size (-b argument) to\n                                   configure. (default: 128)\n--ls-workers <Integer>           Number of Logstash worker threads (-w\n                                   argument) to configure. (default: 2)\n--repeat-data <Integer>          Sets how often the test's dataset should be\n                                   run. (default: 1)\n--testcase <String>              Currently available test cases are 'baseline',\n                                   'apache' and 'custom'. (default: baseline)\n--workdir <File>                 Working directory to store cached files in.\n                                   (default: /Users/d059176/.logstash-\n                                   benchmarks) \n```\n\n##### Example\n\n```bash\n$ java -cp 'benchmark-cli.jar:*' org.logstash.benchmark.cli.Main --workdir=/tmp/benchmark2 --testcase=baseline --distribution-version=5.5.0  \n  ██╗      ██████╗  ██████╗ ███████╗████████╗ █████╗ ███████╗██╗  ██╗          \n  ██║     ██╔═══██╗██╔════╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║  ██║          \n  ██║     ██║   ██║██║  ███╗███████╗   ██║   ███████║███████╗███████║          \n  ██║     ██║   ██║██║   ██║╚════██║   ██║   ██╔══██║╚════██║██╔══██║          \n  ███████╗╚██████╔╝╚██████╔╝███████║   ██║   ██║  ██║███████║██║  ██║          \n  ╚══════╝ ╚═════╝  ╚═════╝ ╚══════╝   ╚═╝   ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝          \n                                                                               \n  ██████╗ ███████╗███╗   ██╗ ██████╗██╗  ██╗███╗   ███╗ █████╗ ██████╗ ██╗  ██╗\n  ██╔══██╗██╔════╝████╗  ██║██╔════╝██║  ██║████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝\n  ██████╔╝█████╗  ██╔██╗ ██║██║     ███████║██╔████╔██║███████║██████╔╝█████╔╝ \n  ██╔══██╗██╔══╝  ██║╚██╗██║██║     ██╔══██║██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗ \n  ██████╔╝███████╗██║ ╚████║╚██████╗██║  ██║██║ ╚═╝ ██║██║  ██║██║  ██║██║  ██╗\n  ╚═════╝ ╚══════╝╚═╝  ╚═══╝ ╚═════╝╚═╝  ╚═╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝\n                                                                               \n  ------------------------------------------\n  Benchmarking Version: 5.5.0\n  Running Test Case: baseline\n  ------------------------------------------\n  Start Time: Sat 7 22 21:28:45.4 2017 CEST\n  Statistical Summary:\n  \n  Elapsed Time: 33s\n  Num Events: 977816\n  Throughput Min: 2000.00\n  Throughput Max: 44500.00\n  Throughput Mean: 37608.31\n  Throughput StdDev: 8985.03\n  Throughput Variance: 80730818.62\n  Mean CPU Usage: 19.27%\n```\n","isInternal":false,"tokens":1212,"sizeBytes":5529},{"name":"README.md","path":"tools/dependencies-report/README.md","rawUrl":"https://raw.githubusercontent.com/elastic/logstash/HEAD/tools/dependencies-report/README.md","title":"dependencies-report Documentation","category":"plugin-manifest","format":"markdown","content":"# Dependency audit tool\n\nThe dependency audit tool automates the verification of the following criteria for all\nthird-party dependencies that are shipped as part of either Logstash core or the [default Logstash \nplugins](https://github.com/elastic/logstash/blob/main/rakelib/plugins-metadata.json):\n* The dependency has been added to the [dependency list file](https://github.com/elastic/logstash/blob/main/tools/dependencies-report/src/main/resources/licenseMapping.csv)\nwith an appropriate project URL and [SPDX license identifier](https://spdx.org/licenses/). \n* The license for the dependency is among those [approved for distribution](https://github.com/elastic/logstash/blob/main/tools/dependencies-report/src/main/resources/acceptableLicenses.csv).\n* There is a corresponding `NOTICE.txt` file in the [notices folder](https://github.com/elastic/logstash/tree/main/tools/dependencies-report/src/main/resources/notices)\ncontaining the appropriate notices or license information for the dependency. These individual \nnotice files will be combined to form the notice file shipped with Logstash.\n\nThe dependency audit tool enumerates all the dependencies, Ruby and Java, direct and transitive,\nfor Logstash core and the default plugins. If any dependencies are found that do not conform to\nthe criteria above, the name of the dependency(ies) along with instructions for resolving are \nprinted to the console and the tool exits with a non-zero return code.\n\nThe dependency audit tool should be run using the script in the `bin` folder:\n\n`$LS_HOME/bin/dependencies-report --csv report.csv`\n","isInternal":false,"tokens":322,"sizeBytes":1590},{"name":"README.md","path":"tools/jvm-options-parser/README.md","rawUrl":"https://raw.githubusercontent.com/elastic/logstash/HEAD/tools/jvm-options-parser/README.md","title":"jvm-options-parser Documentation","category":"plugin-manifest","format":"markdown","content":"Standalone jar for JvmOptionsParser to enable it to run under Java 8 to provide\nhelpful fail fast message when logstash is run older versions of Java.","isInternal":false,"tokens":35,"sizeBytes":150},{"name":"README.md","path":"tools/logstash-docgen/README.md","rawUrl":"https://raw.githubusercontent.com/elastic/logstash/HEAD/tools/logstash-docgen/README.md","title":"logstash-docgen Documentation","category":"plugin-manifest","format":"markdown","content":"**Features**\n\n## Generating the documentation for a Logstash instance\n\nYou can now generate the documentation for a Logstash distribution.\n\n*prerequisite*:\n\n- You need to have installed the plugins inside Logstash, this is necessary to pickup the right file when generating the documentation.\n- You need to have the development gems installed. `bin/logstash-plugin --no-verify --development`\n\nIf you have all of the above you can run this command:\n\n```sh\nrake doc:generate-plugins\nrake doc:generate-plugins[/tmp/the-new-doc] # Depending on the shell you are using you might need to quote the task name, like this rake \"doc:generate-plugins[/tmp/new-doc]\"\n```\n\nThis will give you a output similar to this\n\n```sh\nlogstash-input-file > SUCCESS\nlogstash-input-s3 > SUCCESS\nlogstash-input-kafka > FAIL\n[...]\nExceptions: XXXX\n```\n\nThe generator will try to generate the doc for all the plugins defined in the *Gemfile* and installed in Logstash, if anything goes wrong it won't\nstop the generation of the other plugin. The Task will also report any errors with stacktraces at the end, if one plugin fail the build,\nyou can interrupt the process and it will output the current errors before exiting.\n\n\n## Generating the documentation for all the plugins from the organization\n\nYou can now generate the documentation from main for all the plugin in the *logstash-plugins* organization.\n\n*prerequisite*\n\n- You need to go in the `tools/logstash-docgen` directory\n- You need to have the dependency installed.\n\n To get started you can run the following commands:\n\n ```sh\n cd tools/logstash-docgen\n bundle install\n ```\n\nYou can use the `bin/logstash-docgen` command to generate any plugin that you want, this executable can generate all the plugins or specific one from their main branch.\n\nUsages:\n\n```sh\nbin/logstash-docgen --all # will generate the doc for all the plugins\nbin/logstash-docgen logstash-input-file logstash-input-s3 # generate doc for 2 plugins\n```\n\n**See:** `bin/logstash-docgen --help` for complete usage.\n\n**Notes:**\n- The nature of theses tasks require a lot of external execution to make sure all the doc are done in isolation, this process can take a long time.\n- Some plugins will be skipped, see `logstash-docgen.yml` for details.\n- The script will inject itself as a dependency on the plugin.\n\n\n## Testing the documentation as the plugin author\n\n*prerequisite*\n\n- Declare the `logstash-docgen` as development dependency\n- Add `require \"logstash/docgen/plugin_doc\"` to the `Rakefile`\n- Run `bundle install`\n\nAfter you can have access to a few rake tasks, you can list them with `bundle exec rake -T`\n\n```\nbundle exec rake doc:asciidoc # return the raw asciidoc\nbundle exec rake doc:html # Give you the raw html\n```\n\n## CI test\n\nThe CI can use the `ci/docs.sh` script to correctly bootstrap and execute the docgeneration\n\n\n","isInternal":false,"tokens":652,"sizeBytes":2834},{"name":"README.md","path":"tools/paquet/README.md","rawUrl":"https://raw.githubusercontent.com/elastic/logstash/HEAD/tools/paquet/README.md","title":"paquet Documentation","category":"plugin-manifest","format":"markdown","content":"# Paquet\n\nThis gem allow a developer to create a uber gem, a uber gem is a gem that content the current gem and his dependencies and is distributed as a tarball.\n\nThis tool allow to define what will be bundler and what should be ignored, it uses the dependencies defined in the gemspec and gemfile to know what to download.\n\nNote that by default no gems will be bundled.\n\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'paquet'\n```\n\nAnd then execute:\n\n    $ bundle\n\n## Usage\nDefine the dependencies in your Rakefile\n\n```ruby\nrequire \"paquet\"\n\nTARGET_DIRECTORY = File.join(File.dirname(__FILE__), \"dependencies\")\n\nPaquet::Task.new(TARGET_DIRECTORY) do\n  pack \"manticore\"\n  pack \"launchy\"\n  pack \"gemoji\"\n  pack \"logstash-output-elasticsearch\"\n\n  # Everything not defined here will be assumed to be provided\n  # by the target installation\n  ignore \"logstash-core-plugin-api\"\n  ignore \"logstash-core\"\nend\n```\n\nAnd run\n\n```\nbundle exec rake paquet:vendor\n```\n\nThe dependencies will be downloaded in your target directory.\n\n## Project Principles\n\n* Community: If a newbie has a bad time, it's a bug.\n* Software: Make it work, then make it right, then make it fast.\n* Technology: If it doesn't do a thing today, we can make it do it tomorrow.\n\n## Contributing\n\nAll contributions are welcome: ideas, patches, documentation, bug reports,\ncomplaints, and even something you drew up on a napkin.\n\nProgramming is not a required skill. Whatever you've seen about open source and\nmaintainers or community members  saying \"send patches or die\" - you will not\nsee that here.\n\nIt is more important to me that you are able to contribute.\n\nFor more information about contributing, see the\n[CONTRIBUTING](../CONTRIBUTING.md) file.\n","isInternal":false,"tokens":414,"sizeBytes":1741}],"systemPromptSnippet":"<yakaai_skills repo=\"elastic/logstash\">\n<!-- File: AGENTS.md (Tokens: ~5622 | Category: root-instruction) -->\n# AGENTS.md\n\nThis file provides guidance to coding agents when working with code in this repository.\n\n## Project Overview\n\nLogstash is a server-side data processing pipeline that ingests data from multiple sources, transforms it, and sends it to various destinations. It's part of the Elastic Stack and is a **hybrid Java/Ruby application** using JRuby.\n\n### JRuby and versions\n\n- The JRuby version is defined in **`versions.yml`** (repo root). The project uses **vendored JRuby only** — no external Ruby, rvm, or rbenv is required; Gradle installs JRuby into `vendor/jruby` via the bootstrap task.\n- The gem path includes a Ruby compatibility version segment (e.g. `vendor/bundle/jruby/X.Y.0`). It is intentionally hardcoded in Gradle (`rubyUtils.gradle`), shell scripts (`bin/logstash.lib.sh`), and Java test helpers (e.g. `RubyEnvTestCase`). To find the current value, grep for `vendor/bundle/jruby/` in `bin/logstash.lib.sh`. Do not treat it as a bug or \"dynamicize\" it when working on the codebase.\n\n### Single source of truth for versions\n\n- **`versions.yml`** in the repository root is the single source of truth for Logstash version, JRuby version, bundled JDK, Jackson, and related coordinates. Gradle loads it in `settings.gradle`; the `logstash-core` and `logstash-core-plugin-api` gemspecs copy from it. When in doubt about a version, check or update `versions.yml`.\n\n## Build Commands\n\nRun all build and test commands from the **repository root** (where `gradlew` and `versions.yml` live), unless a specific task or doc says otherwise.\n\nAll commands require a JDK that meets the `sourceCompatibility` defined in `build.gradle` (currently 17+). JDK 21 is preferred and is the version bundled for distribution (see `bundled_jdk` in `versions.yml`). Ruby tests run via vendored JRuby — no external Ruby, rvm, or rbenv needed.\n\nUse Gradle tasks as the normal entry points from the repo root. Rake and bundle are used as leaf tools where the existing Gradle tasks or scripts already wire them up.\n\n```bash\n# Initial setup (required once)\n./gradlew bootstrap\n\n# Install development dependencies\n./gradlew installDevelopmentGems\n\n# Install default plugins (80+ plugins)\n./gradlew installDefaultGems\n\n# Build distribution artifact (tar.gz)\n./gradlew assembleTarDistribution\n# Other distribution formats (zip, deb, rpm) are built via rake tasks in rakelib/artifacts.rake\n```\n\n**Build performance (CI):** For faster `installDefaultGems`, use `JARS_SKIP=true`; required jars are already vendored or in JRuby stdlib.\n\n## Running Tests\n\n**Test layout:** Unit specs live under **`spec/`** (root) and **`logstash-core/spec/`**. Integration and acceptance tests live under **`qa/`** (e.g. **`qa/integration`** for integration tests). Use the commands below from the repo root; the single-integration-test example uses a path under `qa/integration`.\n\n**Test task structure:** There are two separate Gradle test tasks: `javaTests` (pure Java unit tests) and `rubyTests` (Ruby specs **and** Java tests that depend on Ruby infrastructure). The top-level `test` task excludes all classes itself and simply delegates to both, so `./gradlew test --tests ...` will silently match nothing. Always apply `--tests` to `javaTests` or `rubyTests` directly.\n\n**`javaTests` vs `rubyTests` split:** Several Java test classes are excluded from `javaTests` and included in `rubyTests` because they load Ruby code at runtime. The exclude/include lists are in `logstash-core/build.gradle`. If `--tests` returns \"No tests found for given includes\", the class is likely in the other task. Examples of Java tests that live in `rubyTests`:\n- `OutputDelegatorTest`, `JavaCodecDelegatorTest` — test delegators that wrap Ruby plugin instances\n- `ConfigCompilerTest`, `CompiledPipelineTest`, `EventConditionTest` — test config compilation which invokes the Ruby parser\n- `PluginFactoryExtTest` — tests Ruby plugin instantiation\n\n```bash\n# All unit tests (Java + Ruby)\n./gradlew test\n\n# All Java tests (pure Java, no Ruby dependency)\n./gradlew :logstash-core:javaTests\n\n# Single Java test class\n./gradlew :logstash-core:javaTests --tests org.logstash.TimestampTest\n\n# Single Java test method\n./gradlew :logstash-core:javaTests --tests org.logstash.TimestampTest.testEpochMillis\n\n# Java tests by pattern\n./gradlew :logstash-core:javaTests --tests \"org.logstash.settings.*\"\n\n# WRONG — will silently run nothing because `test` excludes all classes:\n# ./gradlew :logstash-core:test --tests org.logstash.TimestampTest\n\n# Java test that depends on Ruby — must use rubyTests, not javaTests:\n./gradlew :logstash-core:rubyTests --tests org.logstash.config.ir.compiler.OutputDelegatorTest\n\n# All Ruby tests (core) — also runs the Ruby-dependent Java tests listed above\n./gradlew :logstash-core:rubyTests\n\n# Single Ruby spec file\nSPEC_OPTS=\"-fd -P logstash-core/spec/logstash/agent_spec.rb\" \\\n  ./gradlew :logstash-core:rubyTests --tests org.logstash.RSpecTests\n\n# All integration tests\n./gradlew runIntegrationTests\n\n# Single integration test\n./gradlew runIntegrationTests -PrubyIntegrationSpecs=specs/slowlog_spec.rb\n\n# All X-Pack Ruby tests\n./gradlew :logstash-xpack:rubyTests\n\n# Single X-Pack Ruby test\nSPEC_OPTS=\"-fd -P spec/config_management/elasticsearch_source_spec.rb\" \\\n  ./gradlew :logstash-xpack:rubyTests --rerun-tasks\n\n# Single Ruby test for core or X-Pack (fast, direct JRuby)\nbin/rspec logstash-core/spec/logstash/some_spec.rb\nbin/rspec x-pack/spec/config_management/elasticsearch_source_spec.rb\n```\n\n## Quick Verification\n\n```bash\n# Start Logstash with inline config\nbin/logstash -e 'input { stdin { } } output { stdout {} }'\n```\n\n## Architecture\n\n### Dual Java/Ruby Runtime\n- **Java Core** (`logstash-core/src/main/java/org/logstash/`): Pipeline execution engine, event processing, persistent queue, configuration compilation\n- **Ruby Core** (`logstash-core/lib/logstash/`): Agent lifecycle, plugin coordination, REST API, configuration loading, metric store, settings management\n\n### Key Components\n- **Agent** (`logstash-core/lib/logstash/agent.rb`): Main orchestrator, manages pipeline lifecycle\n- **JavaPipeline** (`logstash-core/lib/logstash/java_pipeline.rb`): Ruby class for core event processing with worker threads, backed by Java execution infrastructure in `logstash-core/src/main/java/org/logstash/execution/`\n- **Plugin System** (`logstash-core/lib/logstash/plugins/`): Input/Filter/Output/Codec plugin infrastructure\n- **Plugin API** (`logstash-core-plugin-api/`): Public interface for plugin developers\n- **JvmOptionsParser** (`tools/jvm-options-parser/`): Standalone Java 11 subproject that parses `config/jvm.options` and composes JVM command-line flags before JRuby starts. Supports version-conditional syntax (e.g. `11-:`, `16-20:`), resolves `${ENV_VAR}` references, and enforces mandatory JVM flags. Invoked from `bin/logstash.lib.sh` during `setup_java()`.\n\n### Directory Structure\n- `logstash-core/` - Core framework (Java + Ruby)\n- `lib/pluginmanager/` - Plugin installation and management\n- `x-pack/` - Elastic-licensed features (disable with `OSS=true`)\n- `qa/` - Quality assurance: acceptance, integration, Docker tests\n- `tools/` - Development tools (docgen, benchmarks, release automation)\n\n### Gradle–Ruby interaction\n\n- **`rubyUtils.gradle`** drives Ruby from Gradle: it uses JRuby's `ScriptingContainer`, `executeJruby`, and invokes bundle/rake. The vendored JRuby lives under **`vendor/jruby`** after `./gradlew bootstrap`. When debugging Gradle tasks that run Ruby (e.g. plugin install, Rake), this is where the wiring lives.\n\n## Code Design Practices\n\nWhen modifying code, don't just append to what's there — evaluate whether the surrounding code still makes sense with the change. If a method, class, or abstraction was designed for one case and you're adding a second, consider restructuring so the result reads as if both cases were planned from the start. Each distinct concept should have its own clearly-named unit, and higher-level code should compose those units so the intent is obvious without reading the implementation.\n\n### Plugin System Conventions\n\n- **Inherit from the type-specific base class.** Each plugin type has a base in `logstash-core/lib/logstash/{type}s/base.rb` (inputs, filters, outputs, codecs). Override `register` for setup plus the type-specific entry point: `run` (inputs), `filter` (filters), `receive`/`multi_receive` (outputs), `decode`/`encode` (codecs).\n- **Declare configuration with the `config` DSL**, not raw `@params` access. Use `config :name, :validate => :type, :default => value` — defined in `logstash-core/lib/logstash/config/mixin.rb`. The framework handles validation, coercion, and documentation generation from these declarations.\n- **Set output concurrency at the class level** with `concurrency :shared` or `concurrency :single` in `logstash-core/lib/logstash/outputs/base.rb`. This determines which `OutputStrategy` the pipeline uses to wrap the plugin: `:shared` means multiple worker threads can call the plugin simultaneously; `:single` serialises all calls so only one thread executes the plugin at a time. Most new outputs should use `:shared`.\n- **Plugin lookup goes through `LogStash::Plugins::Registry#lookup`** (`logstash-core/lib/logstash/plugins/registry.rb`). New built-in plugins register in `logstash-core/lib/logstash/plugins/builtin.rb`. External plugins are discovered as gems.\n\n### Delegator and Metrics Patterns\n\n- **Every plugin runs behind a delegator** that adds metrics collection and thread-safety. Filters: `FilterDelegatorExt` (Java), outputs: `OutputDelegatorExt` (Java), codecs: `Codecs::Delegator` (`logstash-core/lib/logstash/codecs/delegator.rb`). When changing plugin behavior, determine whether the change belongs in the delegator (cross-cutting concern) or in the plugin itself.\n- **Metrics follow a namespace hierarchy.** Use `metric.namespace(:sub)` to create children. `NullMetric` implements the same interface but discards data — it's the null-object stand-in when `enable_metric => false` or during testing. Never instantiate metric objects directly; accept them from the framework.\n- **Standard mixins are included automatically.** All plugins get `LogStash::Util::Loggable` (provides `logger`, `slow_logger`, `deprecation_logger`) and `LogStash::Config::Mixin` (config DSL). Additional mixins like `ECSCompatibilitySupport` and `EventFactorySupport` are opt-in per plugin.\n\n### Pipeline Execution Model\n\n- **The worker loop is the hot path.** `WorkerLoop` (`logstash-core/src/main/java/org/logstash/execution/WorkerLoop.java`) pulls batches from the queue, runs them through the compiled pipeline, and signals completion. Shutdown is coordinated via `AtomicBoolean` flags — do not add blocking calls or heavy allocations inside this loop.\n- **Pipeline lifecycle follows a strict ordering** in `JavaPipeline` (`logstash-core/lib/logstash/java_pipeline.rb`): outputs and filters register first, then workers start, then inputs start last. On shutdown the reverse applies: inputs stop first, workers drain, then filters and outputs close. Violating this ordering causes data loss or deadlocks.\n- **Use `Concurrent::AtomicBoolean` (Ruby) or `java.util.concurrent.atomic.AtomicBoolean` (Java) for cross-thread signaling.** Do not use plain boolean instance variables for state shared across threads — Ruby's GIL does not guarantee visibility, and JRuby runs without one.\n\n#### Runtime Code Generation (Config → Java Classes)\n\nLogstash configs are compiled into executable Java classes at runtime through a multi-stage pipeline:\n\n1. **Parse:** Treetop grammar (`logstash-core/lib/logstash/compiler/lscl/lscl_grammar.treetop`) parses `.conf` files into an AST.\n2. **IR:** `ConfigCompiler` (`logstash-core/src/main/java/org/logstash/config/ir/ConfigCompiler.java`) converts the AST into a `PipelineIR` — a directed acyclic graph of plugins and conditionals.\n3. **Compile:** `DatasetCompiler` (`logstash-core/src/main/java/org/logstash/config/ir/compiler/DatasetCompiler.java`) generates Java source strings for each pipeline vertex. `ComputeStepSyntaxElement` (`logstash-core/src/main/java/org/logstash/config/ir/compiler/ComputeStepSyntaxElement.java`) compiles these sources into classes via Janino at runtime. Generated classes live under the `org.logstash.generated` package.\n4. **Execute:** `CompiledPipeline` (`logstash-core/src/main/java/org/logstash/execution/CompiledPipeline.java`) wires the compiled datasets into the worker loop.\n\nConditionals from config (`if [field] == \"value\"`) compile into `EventCondition` lambdas wired into `SplitDataset` nodes. To inspect generated source code, add these flags to `config/jvm.options`:\n```\n-Dorg.codehaus.janino.source_debugging.enable=true\n-Dorg.codehaus.janino.source_debugging.dir=/path/to/output\n```\n\n### Java Code Conventions\n\n- **Packages are organized by functional domain**, not architecture layers. Main packages under `org.logstash`: `execution` (pipeline/workers), `config.ir` (config compilation), `plugins` (plugin infrastructure), `ackedqueue` (persistent queue), `instrument.metrics` (metrics), `ext` (JRuby extensions), `settings` (configuration types), `log` (logging). Place new classes in the package matching their domain.\n- **Naming conventions:** JRuby extension classes use the `Ext` suffix (`LoggableExt`, `AbstractSimpleMetricExt`). Delegator classes use `Delegator` suffix. Abstract bases use the `Abstract` prefix. Most concrete classes are `public final class` — use plain `public class` only when subclassing is intended (e.g. `BaseSetting<T>`).\n- **Prefer unchecked exceptions.** Custom exceptions extend `RuntimeException` (e.g. `FieldReference.IllegalSyntaxException`, `Event.InvalidTagsTypeException`). Checked exceptions appear only when unavoidable (`IOException`, `InterruptedException`). Include descriptive messages and preserve exception chains via the `cause` parameter.\n- **Null handling:** Use `Objects.requireNonNull()` for constructor parameter validation. Use `@Nullable` annotation when null is an intentional part of the contract. Use `Optional` for parse/lookup chains that may fail (see `Timestamp.tryParse`), not as a general null replacement. Prefer explicit null checks before dereference over implicit assumptions.\n- **Logging uses Log4j 2.** Declare loggers as `private static final Logger LOGGER = LogManager.getLogger(MyClass.class)`. Use `TRACE` for diagnostic detail (parse attempts, fallback paths), `WARN` for recoverable issues, `ERROR` for uncaught exceptions, `FATAL` for unrecoverable system errors. Include context (thread name, field type, value) in messages.\n- **Thread safety:** Prefer immutable `final` fields as the primary safety mechanism. Use `ConcurrentHashMap` for thread-safe caches (see `FieldReference.CACHE`). Use `AtomicBoolean` for shutdown signaling. Document thread-safety guarantees in class-level JavaDoc when the class is shared across threads.\n\n### REST API Conventions\n\n- **API modules inherit from `LogStash::Api::Modules::Base`** (`logstash-core/lib/logstash/api/modules/base.rb`), which extends `Sinatra::Base`. Each module receives the `agent` reference and a `CommandFactory` in its constructor. Define routes using Sinatra's `get`/`put` DSL inside the module class.\n- **Routing uses rack namespace mapping.** Endpoints are mounted at path prefixes (`/_node`, `/_stats`, `/_node/stats`, `/_node/plugins`, `/_node/logging`, `/_health_report`) defined in `rack_namespaces` in `logstash-core/lib/logstash/api/rack_app.rb`. Add new endpoint namespaces there, not via ad-hoc Sinatra routing.\n- **All responses go through `respond_with`** (`logstash-core/lib/logstash/api/app_helpers.rb`). It automatically wraps JSON responses with default metadata (host, version, http_address, id) unless `exclude_default_metadata` is passed. Use `respond_with(data)` — do not write raw JSON responses.\n- **Errors use `ApiError` subclasses** (`logstash-core/lib/logstash/api/errors.rb`): `NotFoundError` (404), `BadRequest` (400), `RequestTimeout` (408). Raise these from route handlers; the base module catches them and formats a consistent error envelope with `path`, `status`, and `error` fields.\n- **Business logic lives in command classes**, not in route handlers. Commands inherit from `LogStash::Api::Commands::Base` (`logstash-core/lib/logstash/api/commands/base.rb`) and access metrics via `service.extract_metrics(path, *keys)`. Route handlers should only call `factory.build(:command_name)` and pass results to `respond_with`.\n\n### Java/Ruby Boundary\n\n- **JRuby extensions use `@JRubyClass`/`@JRubyMethod` annotations.** Classes register in `RubyUtil` (`logstash-core/src/main/java/org/logstash/RubyUtil.java`) via `setupLogstashClass` or `defineClassUnder`. Follow existing patterns in `RubyUtil`'s static initializer when adding new Java-backed Ruby classes.\n- **`ThreadContext` must be obtained at point of use** via `RubyUtil.RUBY.getCurrentContext()`, never stored at construction time. Storing it causes `NullPointerException` when used from a different thread (JRuby 10+). See `RubyCodecDelegator` for the correct pattern.\n- **Test the round-trip through Java.** Integration tests should exercise the full path from Ruby through the Java layer and back, not just the Ruby classes in isolation. This catches type coercion issues and metric namespace registration bugs that unit tests miss.\n- **Java tests that load Ruby:** Some Java tests (e.g. `RubyEnvTestCase` and subclasses) set **GEM_HOME** / **GEM_PATH** to the vendored bundle path (`vendor/bundle/jruby/<ruby-compat-version>`) so they can load Logstash Ruby code. Check `bin/logstash.lib.sh` or `rubyUtils.gradle` for the current version segment. That path is intentional; do not change it when touching those tests.\n\n### Test Practices\n\n- **Use `sample_one` helper for filter pipeline tests.** `PipelineHelpers#sample_one` (`logstash-core/spec/support/pipeline/pipeline_helpers.rb`) spins up a real pipeline, feeds events through a filter config, and captures results — preferred over manually constructing pipeline objects.\n- **Create dummy plugin classes inside spec files for isolation.** Define throwaway classes inheriting from the appropriate base, register with `LogStash::PLUGIN_REGISTRY.add`. See `pipeline_helpers.rb` for examples.\n- **Use `shared_examples` for interface contracts and `shared_context` for common setup.** Examples: `\"metrics commons operations\"` in `logstash-core/spec/support/shared_examples.rb`, `\"execution_context\"` in `logstash-core/spec/support/shared_contexts.rb`.\n- **Integration tests use the Fixture + Service pattern.** `Fixture` (`qa/integration/framework/fixture.rb`) bootstraps services and loads config from YAML fixture files under `qa/integration/fixtures/`.\n- **Test that existing rescue blocks catch new code paths.** When a method has a `rescue => e` that returns `nil`, verify that exceptions raised in newly added branches are caught by it. It is easy to accidentally add code outside the `begin...rescue` scope.\n- **Clean up JVM state in `after` blocks.** Any `java.lang.System.setProperty` call in a test must have a corresponding `clearProperty` in `after` to avoid leaking state to other specs.\n- **Prefer concrete test doubles over mocks.** The codebase uses purpose-built classes (`DummyInput`, `DummyFilter`, `DummyOutput` in `logstash-core/spec/support/mocks_classes.rb`) that inherit from real base classes with minimal implementations. Use these over RSpec `double()` when testing pipeline behavior — they exercise real plugin lifecycle methods.\n- **Reserve mocks for infrastructure boundaries.** `LogStash::Agent`, loggers, and `LogStash::SETTINGS` are commonly stubbed to prevent real pipeline startup. Use `allow(obj).to receive(:method).and_call_original` when you need the mock for setup but want to test real behavior. The codebase uses plain `double()` and `.as_null_object` — not `instance_double` or `class_double` — due to JRuby's dynamic method dispatch.\n\n### Test Validity\n\n- **Verify every new test fails without the corresponding change.** Before treating a test as done, confirm it fails against the pre-change code. A test that passes with or without the fix proves nothing. This is the most basic check that a test is load-bearing.\n- **Assert on the specific behaviour the change introduces, not on pre-existing conditions.** If the system already returned `200 OK` before your fix, asserting `200 OK` does not validate the fix. Pin the assertion to an observable difference — a new field, a changed value, a previously-missing log line.\n- **For bug fixes, reproduce the bug in the test first.** Write the test, watch it fail for the same reason the bug manifests, then apply the fix and watch it pass. If you cannot make the test fail, either the bug is not what you think or the test is not targeting it.\n- **Beware of tautological mocks.** Stubbing a method to return the value you then assert is a circular proof. Mocks should simulate collaborators, not replace the code under test. If removing the implementation and keeping only the mock still passes the test, the test is testing the mock.\n\n## Plugin Development\n\nPlugins live in separate repositories under [logstash-plugins](https://github.com/logstash-plugins). Each plugin is a self-contained Ruby gem. Plugin types: input, filter, output, codec.\n\n- The **`logstash-core-plugin-api`** version is in **`versions.yml`**; plugins (in separate repos) depend on it.\n- Templates for new plugins are in **`lib/pluginmanager/templates/`**.\n- Supported plugin versions and compatibility are tracked in the [logstash-plugins `.ci`](https://github.com/logstash-plugins/.ci) repo (e.g. `logstash-versions.yml`).\n\n## Sensitive Data Handling\n\nWhen adding configuration parameters for sensitive data:\n- In Ruby: Apply `:password` validation or wrap with `Logstash::Util::Password`\n- In Java: See `Password.java` and `SecretVariable.java` for patterns\n- Override `toString()` to mask sensitive values\n- Never log secrets at any log level by default\n\nExample:\n```ruby\n:my_auth => { :validate => :password }\n# Or wrap manually:\n::LogStash::Util::Password.new(my_auth)\n```\n\n## Environment Variables\n\n```bash\nexport LOGSTASH_SOURCE=1\nexport LOGSTASH_PATH=/path/to/logstash\nexport JAVA_HOME=/path/to/jdk  # JDK 17+ (see build.gradle sourceCompatibility)\nexport OSS=true                 # Build without X-Pack\n```\n\n## Debugging\n\n```bash\n# Attach debugger to running Logstash\nLS_JAVA_OPTS=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005\" bin/logstash ...\n```\n\n## Breaking Changes Policy\n\n1. Implement new behavior as opt-in (MINOR)\n2. Add deprecation warnings (MINOR)\n3. Change default, keep opt-out option (MAJOR)\n4. Remove old behavior (MAJOR)\n\n## Documentation\n\n### Directory Structure\n- `docs/reference/` - Main user-facing documentation (Markdown)\n- `docs/extend/` - Plugin development and contribution guides (Markdown)\n- `docs/release-notes/` - Version-specific release notes, breaking changes, deprecations\n- `docs/static/` - Static assets (images, OpenAPI specs)\n- `tools/logstash-docgen/` - Auto-generates plugin documentation from code\n\n### Formats\n- **Reference docs**: Markdown (`.md`) with front matter and admonition syntax\n- **Plugin docs**: AsciiDoc (`.asciidoc`) with dynamic ID variables\n\n### Markdown Conventions (Reference Docs)\n```markdown\n---\nmapped_pages:\n  - https://www.elastic.co/guide/en/logstash/current/page.html\n---\n\n# Heading Title [anchor-id]\n\n::::{admonition} Note title\n:class: note\nContent here\n::::\n```\n\n### AsciiDoc Conventions (Plugin Docs)\n```asciidoc\n:plugin: example\n:type: filter\n\n[id=\"plugins-{type}s-{plugin}\"]\n=== Example filter plugin\n\n[id=\"plugins-{type}s-{plugin}-options\"]\n==== Configuration Options\n```\n\nUse dynamic IDs (`{type}`, `{plugin}`) to prevent ID duplication across plugin versions.\n\n### Guidelines\n- All PRs must include documentation updates when applicable\n- Plugin documentation is required before merging\n- Follow Elastic documentation standards: one sentence per line in AsciiDoc, use `[id=\"...\"]` anchors (not Markdown-style), prefer `[source,lang]` for code blocks, use `<<anchor>>` for cross-references, and keep titles in title case\n- Images go in `docs/reference/images/` (PNG format)\n\n## See also\n\n- **CONTRIBUTING.md** — Contribution process, sensitive data, and where to ask questions\n- **STYLE.md** — Ruby style (indentation, logging, hash syntax) and consistency\n\n<!-- File: qa/AGENTS.md (Tokens: ~1607 | Category: root-instruction) -->\n# qa/AGENTS.md\n\nGuidance for coding agents working with the QA test infrastructure. See also the root `AGENTS.md` for general project conventions.\n\n## Overview\n\nThe `qa/` directory contains three independent test suites:\n\n| Suite | Path | Purpose |\n|-------|------|---------|\n| **Integration** | `qa/integration/` | End-to-end tests with live services (Elasticsearch, Kafka, etc.) |\n| **Acceptance** | `qa/acceptance/` | Package artifact testing (deb, rpm install/upgrade) |\n| **Docker** | `qa/docker/` | Docker image and container verification |\n\nEach suite has its own dependencies and execution workflow.\n\n## Integration Tests (qa/integration/)\n\n### Running\n\n```bash\n# All integration tests via Gradle (handles all setup automatically)\n./gradlew runIntegrationTests\n\n# Single test\n./gradlew runIntegrationTests -PrubyIntegrationSpecs=specs/dlq_spec.rb\n\n# Manual: set up artifacts first, then run directly from qa/integration/\n./gradlew installIntegrationTestGems  # build Logstash tarball + install test gems\n./gradlew copyEs                      # download Elasticsearch (if spec needs it)\n./gradlew copyFilebeat                # download Filebeat (if spec needs it)\ncd qa/integration\nBUNDLE_PATH=../../build/qa/integration/vendor \\\n  ../../vendor/jruby/bin/jruby -S bundle exec rspec specs/dlq_spec.rb\n```\n\nWhich artifact tasks to run depends on the `services:` list in each spec's fixture YAML.\n### Architecture: Fixture + Service Pattern\n\nEvery integration test has a matching YAML fixture file:\n- Test: `qa/integration/specs/dlq_spec.rb`\n- Fixture: `qa/integration/fixtures/dlq_spec.yml`\n\n**Fixture YAML structure:**\n```yaml\n---\nservices:\n  - logstash\n  - elasticsearch\nconfig: |-\n  input { generator { count => 100 } }\n  output { elasticsearch { index => \"test\" } }\n```\n\nFixtures can also use ERB for dynamic values and define multiple named configs as a hash.\n\n**In test code:**\n```ruby\n@fixture = Fixture.new(__FILE__)           # Loads matching YAML\n@logstash = @fixture.get_service(\"logstash\")\nconfig = @fixture.config(\"root\", { :port => 9200 })  # ERB interpolation\n```\n\n### Key Framework Classes\n\n- **`Fixture`** (`framework/fixture.rb`) — Bootstraps services and loads config from YAML fixtures.\n- **`TestSettings`** (`framework/settings.rb`) — Merges per-test YAML with global `suite.yml`, performs ERB expansion.\n- **`LogstashService`** (`services/logstash_service.rb`) — Manages a Logstash process from the built tarball. Key methods: `start_with_config_string(config)`, `start_background(config_file)`, `wait_for_logstash`, `teardown`.\n- **`MonitoringAPI`** (`services/monitoring_api.rb`) — HTTP wrapper for the Logstash REST API (`pipeline_stats`, `node_info`, `health_report`).\n- **Helpers** (`framework/helpers.rb`) — `wait_for_port`, `is_port_open?`, `send_data`, `config_to_temp_file`, `random_port`.\n\n### Service Management\n\nServices are auto-discovered by naming convention (`*_service.rb` in `services/`). Each service consists of:\n1. A Ruby class extending `Service` (e.g. `ElasticsearchService`)\n2. A `<name>_setup.sh` script (called during test setup)\n3. A `<name>_teardown.sh` script (called during cleanup)\n\nTo add a new service: create all three files in `qa/integration/services/` and reference the service name in your fixture YAML.\n\n### Test File Pattern\n\n```ruby\nrequire_relative '../framework/fixture'\nrequire_relative '../framework/settings'\nrequire_relative '../services/logstash_service'\n\ndescribe \"Feature under test\" do\n  before(:all) {\n    @fixture = Fixture.new(__FILE__)\n    @logstash = @fixture.get_service(\"logstash\")\n  }\n\n  after(:all) { @fixture.teardown }\n  after(:each) { @logstash.teardown }\n\n  it \"should behave correctly\" do\n    @logstash.start_with_config_string(@fixture.config)\n    # ... assertions ...\n  end\nend\n```\n\n### Dependencies\n\nDefined in `qa/integration/integration_tests.gemspec`.\n\n## Acceptance Tests (qa/acceptance/)\n\n### Purpose\n\nVerify that Logstash packages (`.deb`, `.rpm`) install, start, and upgrade correctly on target platforms. These test the artifact, not pipeline behavior.\n\nThese tests assume a Linux environment with `/etc/os-release` and distro package tools; they are not meant to run natively on macOS.\n\n### Running\n\n```bash\n# All acceptance tests (from repo root; uses vendored JRuby)\n./gradlew runAcceptanceTests\n\n# Or install gems once, then run a single test directly\n./gradlew installAcceptanceTestGems\ncd qa\nBUNDLE_PATH=vendor/bundle \\\n  ../vendor/jruby/bin/jruby -S bundle exec rspec acceptance/spec/lib/artifact_operation_spec.rb\n```\n\n### Architecture\n\nTests use RSpec `shared_examples` for reusable test sequences:\n- `\"installable\"` — Download, install, start, stop\n- `\"installable_with_jdk\"` — Install and verify bundled JDK\n- `\"runnable\"` — Verify process is running\n- `\"updated\"` — Test upgrade from previous version\n\nShared examples live in `qa/acceptance/spec/shared_examples/`.\n\n## Docker Tests (qa/docker/)\n\n### Purpose\n\nVerify Docker image build correctness (labels, working directory, architecture), container startup, health checks, and X-Pack features.\n\n### Running\n\nThe tests find images by searching local Docker for `docker.elastic.co/logstash/logstash-{flavor}:{version}-SNAPSHOT`. Build the image first, then run tests. `ARCH` defaults to `x86_64`; set `ARCH=aarch64` when building arm64 images. If you run tests against an image whose architecture differs from the host, set `DOCKER_ARCHITECTURE=amd64|arm64` so the spec helpers compare against the correct target architecture.\n```bash\n# Build images (from repo root)\n./gradlew artifactDocker         # full\n./gradlew artifactDockerOss      # oss\n./gradlew artifactDockerWolfi    # wolfi\n./gradlew artifactDockerIronbank # ironbank\n\n# Run tests through Gradle (build + vendored JRuby)\n./gradlew runAllDockerTests\n./gradlew runDockerFullTests     # or runDockerOssTests / runDockerWolfiTests / runDockerIronbankTests\n\n# Or run directly from qa after installAcceptanceTestGems\ncd qa\nBUNDLE_PATH=vendor/bundle \\\n  ../vendor/jruby/bin/jruby -S bundle exec rspec docker/spec/full\n```\n\n### Architecture\n\nTests use shared examples across image flavors (`full`, `oss`, `wolfi`, `ironbank`). Key helpers in `spec/spec_helper.rb`: `find_image`, `start_container`, `exec_in_container`, `wait_for_logstash`, `cleanup_container`.\n\n## Debugging\n\n```bash\n# Integration: verbose output\ncd qa/integration\nVERBOSE=true BUNDLE_PATH=../../build/qa/integration/vendor \\\n  ../../vendor/jruby/bin/jruby -S bundle exec rspec specs/my_spec.rb -fd\n\n# Docker: inspect container\ndocker run -it logstash-test /bin/bash\n```\n\n<!-- File: x-pack/AGENTS.md (Tokens: ~1158 | Category: root-instruction) -->\n# x-pack/AGENTS.md\n\nGuidance for coding agents working with X-Pack (Elastic-licensed) features. See also the root `AGENTS.md` for general project conventions.\n\n## Overview\n\nX-Pack provides commercial features that extend the Logstash core. Most features integrate via the **UniversalPlugin** extension pattern; License Checking is a standalone library used by the other features. X-Pack is a separate Ruby package under `x-pack/` with its own test suite. Build without X-Pack by setting `OSS=true`.\n\n## Features\n\n| Feature | Path | Purpose |\n|---------|------|---------|\n| **Monitoring** | `lib/monitoring/` | Collects JVM, system, and pipeline metrics; ships to Elasticsearch |\n| **Config Management** | `lib/config_management/` | Fetches pipeline configs from Elasticsearch/Kibana |\n| **GeoIP Database Management** | `lib/geoip_database_management/` | Auto-downloads and updates GeoIP databases from Elastic CDN |\n| **License Checking** | `lib/license_checker/` | Validates Elasticsearch license for feature gating |\n\n## Extension Pattern\n\nMonitoring, Config Management, and GeoIP follow the same integration pattern. Each has an **extension class** inheriting from `LogStash::UniversalPlugin` that implements two methods:\n\n1. **`additionals_settings(settings)`** — Registers `xpack.*` configuration settings with the core settings registry.\n2. **`register_hooks(hooks)`** — Registers lifecycle callbacks with `LogStash::Runner` (e.g. `before_bootstrap_checks`, `after_bootstrap_checks`).\n\n**Entry point:** `lib/x-pack/logstash_registry.rb` registers all three extensions plus built-in input/output plugins with `LogStash::PLUGIN_REGISTRY`.\n\n**Extension files:**\n- `lib/config_management/extension.rb`\n- `lib/geoip_database_management/extension.rb`\n- `lib/monitoring/monitoring.rb` (extension at bottom of file)\n\n### Adding New Settings\n\nUse `LogStash::Setting::*` classes in `additionals_settings`:\n\n```ruby\ndef additionals_settings(settings)\n  settings.register(LogStash::Setting::BooleanSetting.new(\"xpack.feature.enabled\", false))\n  settings.register(LogStash::Setting::TimeValueSetting.new(\"xpack.feature.interval\", \"5s\"))\n  settings.register(LogStash::Setting::ArrayCoercible.new(\"xpack.feature.hosts\", String, [\"localhost\"]))\n  settings.register(LogStash::Setting::NullableStringSetting.new(\"xpack.feature.password\"))\nend\n```\n\nAll X-Pack settings use the `xpack.` prefix. Elasticsearch connection options (hosts, SSL, auth, proxy) are shared across features via the `ElasticsearchOptions` helper mixin.\n\n## License Checking\n\nFeatures that require a commercial license use the `Licensed` mixin (`lib/license_checker/licensed.rb`):\n\n1. Call `setup_license_checker(FEATURE_NAME)` during initialization.\n2. Wrap feature logic in `with_license_check(raise_on_error) { ... }`.\n3. Override `populate_license_state(xpack_info, is_serverless)` to return `{ :state => :ok | :error, :log_level => ..., :log_message => ... }`.\n\nThe `LicenseManager` (`lib/license_checker/license_manager.rb`) polls Elasticsearch every 30 seconds and notifies observers on state changes. License types: `trial`, `basic`, `standard`, `gold`, `platinum`, `enterprise`. Config management requires trial or above (not basic).\n\n## Running Tests\n\n```bash\n# Unit tests (from repo root)\n./gradlew :logstash-xpack:rubyTests\n\n# Integration tests (requires running Elasticsearch)\n./gradlew :logstash-xpack:rubyIntegrationTests\n\n# Single integration spec\n./gradlew :logstash-xpack:rubyIntegrationTests \\\n  -PrubyIntegrationSpecs=qa/integration/management/multiple_pipelines_spec.rb\n```\n\n### Test Structure\n\n- **Unit specs:** `x-pack/spec/` — Organized by feature (`spec/monitoring/`, `spec/config_management/`, `spec/geoip_database_management/`, `spec/license_checker/`).\n- **Integration tests:** `x-pack/qa/integration/` — Subdirectories for `management/`, `monitoring/`, and `fips-validation/`.\n- **Test helpers:** `x-pack/spec/support/helpers.rb` and `x-pack/spec/support/matchers.rb`.\n- **Test runners:** JUnit-based RSpec invokers in `x-pack/src/test/java/org/logstash/xpack/test/` (`RSpecTests.java` for unit, `RSpecIntegrationTests.java` for integration).\n\n### GeoIP Test Data\n\nThe `unzipGeolite` Gradle task downloads GeoLite2 test databases. Unit tests for GeoIP depend on this task running first (handled automatically by the `rubyTests` dependency chain).\n\n## Key Architectural Patterns\n\n- **Hook-based integration.** Config management intercepts bootstrap checks to swap the local config source with an Elasticsearch source. Monitoring adds an internal pipeline after the agent starts. Neither modifies core code directly.\n- **Singleton managers.** `GeoipDatabaseManagement::Manager` and `LicenseChecker::LicenseManager` are singletons with thread-safe initialization via Mutex.\n- **Observer pattern.** Database subscriptions and license managers notify observers on state changes, enabling features to react dynamically without polling.\n- **Internal pipelines.** Monitoring generates its pipeline config from an ERB template (`lib/template.cfg.erb`) and injects it via `InternalPipelineSource`. These run alongside user pipelines but are hidden from the user-facing API.\n\n<!-- File: CLAUDE.md (Tokens: ~6 | Category: claude-rule) -->\nSee `AGENTS.md`.\n\n<!-- File: qa/CLAUDE.md (Tokens: ~6 | Category: claude-rule) -->\nSee `AGENTS.md`.\n\n<!-- File: x-pack/CLAUDE.md (Tokens: ~6 | Category: claude-rule) -->\nSee `AGENTS.md`.\n\n<!-- File: tools/benchmark-cli/README.md (Tokens: ~1212 | Category: plugin-manifest) -->\n### Benchmark CLI\n\n#### Build\n\nTo build a self-contained archive of the benchmark tool simply run:\n\n```bash\ngradle clean assemble\n```\n\nwhich will create the output jar under `build/libs/benchmark-cli.jar`.\n\n#### Running\n\n```bash\n$ java -cp 'benchmark-cli.jar:*' org.logstash.benchmark.cli.Main --help\nOption                           Description\n------                           -----------\n--config <File>                  Path to custom logstash config. Required if\n                                   testcase is set to 'custom'\n--data <File>                    Path to custom logstash data. Only if testcase\n                                   is set to 'custom'\n--distribution-version <String>  The version of a Logstash build to download\n                                   from elastic.co.\n--elasticsearch-export <String>  Optional Elasticsearch host URL to store\n                                   detailed results at. (default: )\n--git-hash <String>              Either a git tree (tag/branch or commit hash),\n                                   optionally prefixed by a Github username,\n                                 if ran against forks.\n                                 E.g.\n                                   'ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7',\n                                 'user-\n                                   name#ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7'\n                                   or 'main'\n--local-path <String>            Path to the root of a local Logstash\n                                   distribution.\n                                  E.g. `/opt/logstash`\n--ls-batch-size <Integer>        Logstash batch size (-b argument) to\n                                   configure. (default: 128)\n--ls-workers <Integer>           Number of Logstash worker threads (-w\n                                   argument) to configure. (default: 2)\n--repeat-data <Integer>          Sets how often the test's dataset should be\n                                   run. (default: 1)\n--testcase <String>              Currently available test cases are 'baseline',\n                                   'apache' and 'custom'. (default: baseline)\n--workdir <File>                 Working directory to store cached files in.\n                                   (default: /Users/d059176/.logstash-\n                                   benchmarks) \n```\n\n##### Example\n\n```bash\n$ java -cp 'benchmark-cli.jar:*' org.logstash.benchmark.cli.Main --workdir=/tmp/benchmark2 --testcase=baseline --distribution-version=5.5.0  \n  ██╗      ██████╗  ██████╗ ███████╗████████╗ █████╗ ███████╗██╗  ██╗          \n  ██║     ██╔═══██╗██╔════╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║  ██║          \n  ██║     ██║   ██║██║  ███╗███████╗   ██║   ███████║███████╗███████║          \n  ██║     ██║   ██║██║   ██║╚════██║   ██║   ██╔══██║╚════██║██╔══██║          \n  ███████╗╚██████╔╝╚██████╔╝███████║   ██║   ██║  ██║███████║██║  ██║          \n  ╚══════╝ ╚═════╝  ╚═════╝ ╚══════╝   ╚═╝   ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝          \n                                                                               \n  ██████╗ ███████╗███╗   ██╗ ██████╗██╗  ██╗███╗   ███╗ █████╗ ██████╗ ██╗  ██╗\n  ██╔══██╗██╔════╝████╗  ██║██╔════╝██║  ██║████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝\n  ██████╔╝█████╗  ██╔██╗ ██║██║     ███████║██╔████╔██║███████║██████╔╝█████╔╝ \n  ██╔══██╗██╔══╝  ██║╚██╗██║██║     ██╔══██║██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗ \n  ██████╔╝███████╗██║ ╚████║╚██████╗██║  ██║██║ ╚═╝ ██║██║  ██║██║  ██║██║  ██╗\n  ╚═════╝ ╚══════╝╚═╝  ╚═══╝ ╚═════╝╚═╝  ╚═╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝\n                                                                               \n  ------------------------------------------\n  Benchmarking Version: 5.5.0\n  Running Test Case: baseline\n  ------------------------------------------\n  Start Time: Sat 7 22 21:28:45.4 2017 CEST\n  Statistical Summary:\n  \n  Elapsed Time: 33s\n  Num Events: 977816\n  Throughput Min: 2000.00\n  Throughput Max: 44500.00\n  Throughput Mean: 37608.31\n  Throughput StdDev: 8985.03\n  Throughput Variance: 80730818.62\n  Mean CPU Usage: 19.27%\n```\n\n<!-- File: tools/dependencies-report/README.md (Tokens: ~322 | Category: plugin-manifest) -->\n# Dependency audit tool\n\nThe dependency audit tool automates the verification of the following criteria for all\nthird-party dependencies that are shipped as part of either Logstash core or the [default Logstash \nplugins](https://github.com/elastic/logstash/blob/main/rakelib/plugins-metadata.json):\n* The dependency has been added to the [dependency list file](https://github.com/elastic/logstash/blob/main/tools/dependencies-report/src/main/resources/licenseMapping.csv)\nwith an appropriate project URL and [SPDX license identifier](https://spdx.org/licenses/). \n* The license for the dependency is among those [approved for distribution](https://github.com/elastic/logstash/blob/main/tools/dependencies-report/src/main/resources/acceptableLicenses.csv).\n* There is a corresponding `NOTICE.txt` file in the [notices folder](https://github.com/elastic/logstash/tree/main/tools/dependencies-report/src/main/resources/notices)\ncontaining the appropriate notices or license information for the dependency. These individual \nnotice files will be combined to form the notice file shipped with Logstash.\n\nThe dependency audit tool enumerates all the dependencies, Ruby and Java, direct and transitive,\nfor Logstash core and the default plugins. If any dependencies are found that do not conform to\nthe criteria above, the name of the dependency(ies) along with instructions for resolving are \nprinted to the console and the tool exits with a non-zero return code.\n\nThe dependency audit tool should be run using the script in the `bin` folder:\n\n`$LS_HOME/bin/dependencies-report --csv report.csv`\n\n<!-- File: tools/jvm-options-parser/README.md (Tokens: ~35 | Category: plugin-manifest) -->\nStandalone jar for JvmOptionsParser to enable it to run under Java 8 to provide\nhelpful fail fast message when logstash is run older versions of Java.\n\n<!-- File: tools/logstash-docgen/README.md (Tokens: ~652 | Category: plugin-manifest) -->\n**Features**\n\n## Generating the documentation for a Logstash instance\n\nYou can now generate the documentation for a Logstash distribution.\n\n*prerequisite*:\n\n- You need to have installed the plugins inside Logstash, this is necessary to pickup the right file when generating the documentation.\n- You need to have the development gems installed. `bin/logstash-plugin --no-verify --development`\n\nIf you have all of the above you can run this command:\n\n```sh\nrake doc:generate-plugins\nrake doc:generate-plugins[/tmp/the-new-doc] # Depending on the shell you are using you might need to quote the task name, like this rake \"doc:generate-plugins[/tmp/new-doc]\"\n```\n\nThis will give you a output similar to this\n\n```sh\nlogstash-input-file > SUCCESS\nlogstash-input-s3 > SUCCESS\nlogstash-input-kafka > FAIL\n[...]\nExceptions: XXXX\n```\n\nThe generator will try to generate the doc for all the plugins defined in the *Gemfile* and installed in Logstash, if anything goes wrong it won't\nstop the generation of the other plugin. The Task will also report any errors with stacktraces at the end, if one plugin fail the build,\nyou can interrupt the process and it will output the current errors before exiting.\n\n\n## Generating the documentation for all the plugins from the organization\n\nYou can now generate the documentation from main for all the plugin in the *logstash-plugins* organization.\n\n*prerequisite*\n\n- You need to go in the `tools/logstash-docgen` directory\n- You need to have the dependency installed.\n\n To get started you can run the following commands:\n\n ```sh\n cd tools/logstash-docgen\n bundle install\n ```\n\nYou can use the `bin/logstash-docgen` command to generate any plugin that you want, this executable can generate all the plugins or specific one from their main branch.\n\nUsages:\n\n```sh\nbin/logstash-docgen --all # will generate the doc for all the plugins\nbin/logstash-docgen logstash-input-file logstash-input-s3 # generate doc for 2 plugins\n```\n\n**See:** `bin/logstash-docgen --help` for complete usage.\n\n**Notes:**\n- The nature of theses tasks require a lot of external execution to make sure all the doc are done in isolation, this process can take a long time.\n- Some plugins will be skipped, see `logstash-docgen.yml` for details.\n- The script will inject itself as a dependency on the plugin.\n\n\n## Testing the documentation as the plugin author\n\n*prerequisite*\n\n- Declare the `logstash-docgen` as development dependency\n- Add `require \"logstash/docgen/plugin_doc\"` to the `Rakefile`\n- Run `bundle install`\n\nAfter you can have access to a few rake tasks, you can list them with `bundle exec rake -T`\n\n```\nbundle exec rake doc:asciidoc # return the raw asciidoc\nbundle exec rake doc:html # Give you the raw html\n```\n\n## CI test\n\nThe CI can use the `ci/docs.sh` script to correctly bootstrap and execute the docgeneration\n\n<!-- File: tools/paquet/README.md (Tokens: ~414 | Category: plugin-manifest) -->\n# Paquet\n\nThis gem allow a developer to create a uber gem, a uber gem is a gem that content the current gem and his dependencies and is distributed as a tarball.\n\nThis tool allow to define what will be bundler and what should be ignored, it uses the dependencies defined in the gemspec and gemfile to know what to download.\n\nNote that by default no gems will be bundled.\n\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'paquet'\n```\n\nAnd then execute:\n\n    $ bundle\n\n## Usage\nDefine the dependencies in your Rakefile\n\n```ruby\nrequire \"paquet\"\n\nTARGET_DIRECTORY = File.join(File.dirname(__FILE__), \"dependencies\")\n\nPaquet::Task.new(TARGET_DIRECTORY) do\n  pack \"manticore\"\n  pack \"launchy\"\n  pack \"gemoji\"\n  pack \"logstash-output-elasticsearch\"\n\n  # Everything not defined here will be assumed to be provided\n  # by the target installation\n  ignore \"logstash-core-plugin-api\"\n  ignore \"logstash-core\"\nend\n```\n\nAnd run\n\n```\nbundle exec rake paquet:vendor\n```\n\nThe dependencies will be downloaded in your target directory.\n\n## Project Principles\n\n* Community: If a newbie has a bad time, it's a bug.\n* Software: Make it work, then make it right, then make it fast.\n* Technology: If it doesn't do a thing today, we can make it do it tomorrow.\n\n## Contributing\n\nAll contributions are welcome: ideas, patches, documentation, bug reports,\ncomplaints, and even something you drew up on a napkin.\n\nProgramming is not a required skill. Whatever you've seen about open source and\nmaintainers or community members  saying \"send patches or die\" - you will not\nsee that here.\n\nIt is more important to me that you are able to contribute.\n\nFor more information about contributing, see the\n[CONTRIBUTING](../CONTRIBUTING.md) file.\n</yakaai_skills>"}