Expanded Built-In Sinks
Use the expanded sink set when LoadStrike run data should flow into supported observability platforms, databases, message streams, StatsD collectors, JSONL files, or a generic webhook receiver.
Matching docs
Search across docs titles, summaries, groups, and section headings.
Use Up and Down Arrow to move through results, then press Enter to open the active page.
No indexed docs matched that search. Try a broader term or open the docs hub.
What this page helps you do
What this page helps you do
Use the expanded sink set when LoadStrike run data should flow into supported observability platforms, databases, message streams, StatsD collectors, JSONL files, or a generic webhook receiver.
Who this is for
Teams exporting final run data and realtime metrics into supported observability backends.
Prerequisites
- A run result or sink destination you want to wire into the wider observability stack
By the end
A sink-specific setup path that stays tied to the same LoadStrike report model.
Use this page when
Use this page when the local report is not the only destination and you need to wire realtime or final export into a supported sink.
Visual guide
Sample Report Data Rows
Scope Scenario Result Count RPS LatencyP50Ms LatencyP80Ms LatencyP85Ms LatencyP90Ms LatencyP95Ms LatencyP99Ms
Scenario reports-demo OK 675 15.0 21.4 30.2 33.0 36.8 48.6 72.1
LatencyTable Scenario Result Count LatencyP50Ms LatencyP95Ms
LatencyTable reports-demo OK 675 21.4 48.6
LatencyTable reports-demo FAIL 12 35.9 79.2
StatusCode Result Percent
200 OK 97.48
500 FAIL 2.52
FailedStatus Scope Scenario Step StatusCode Count Percent
FailedStatus Scenario reports-demo 500 12 1.75
Reporting
Realtime reporting
Choose Portal Reporting when users should review runs in the customer portal, pick the built-in sink page that matches the backend your team already runs, or open Custom Reporting when you need to implement your own destination. Each tab opens a dedicated page with the settings, behavior, and lifecycle details for that reporting path.
Guide
What The Expanded Set Covers
The expanded sink set covers Prometheus Remote Write, Amazon CloudWatch, Dynatrace, New Relic, Elasticsearch, OpenSearch, Kafka, StatsD, DogStatsD, Netdata-compatible StatsD, JSONL file streaming, and GenericWebhook. These sit alongside InfluxDB, TimescaleDB, Grafana Loki, Datadog, Splunk HEC, OTEL Collector, portal reporting, and custom sink options.
Shared Export Model
Built-in sinks receive the common LoadStrike reporting model: individual attempt batches where the destination supports event records, one final event per completed correlation outcome, realtime scenario stats, projected metrics, final run summaries, final metric snapshots, threshold results, report-file metadata, sink error metadata, and detailed report rows when the run produces them. Correlation outcome events retain tracking and GatherBy dimensions without substituting SDK percentile aggregates. Metric-only destinations expose the closest supported per-attempt projection and report that the complete event shape is unavailable. Choose the destination based on where your team already reviews performance evidence.
Destination Families
Prometheus Remote Write sends Snappy-compressed protobuf; Amazon CloudWatch sends SigV4-signed PutMetricData requests; Dynatrace sends its metrics line protocol; New Relic sends Metric API JSON batches. These are metrics-first destinations. GenericWebhook instead sends generic LoadStrike reporting JSON. Use StatsD, DogStatsD, or Netdata-compatible StatsD for UDP metric workflows and JSONL for a portable event stream. Elasticsearch and OpenSearch use one JSON document per reporting call. Use Kafka when the organization wants reporting events on an internal stream for downstream processing.
Line-Oriented Destinations
StatsD-compatible prefixes and DogStatsD tag values normalize unsupported protocol separators so one measurement cannot become extra records. InfluxDB normalizes line breaks in names, tags, and string fields to spaces before escaping them; ordinary single-line values are preserved.
Direct Vendor Availability
Prometheus Remote Write, Amazon CloudWatch, Dynatrace, and New Relic are supported in C#, Java, Python, TypeScript, JavaScript, and Go with provider-specific payloads. Python requires the reporting extra for Prometheus and CloudWatch. Java requires provider options plus a ProtocolPublisher.
Java Publisher Requirement
A Java ProtocolPublisher receives the complete target, headers, and byte body and must transmit all three. It also owns HTTP retry, TLS, and proxy policy. Initialization fails with `<SinkClass> requires a ProtocolPublisher.` when it is absent. Replace deprecated no-argument vendor construction with the provider options plus this publisher.
C# Native Kafka And UDP
C# Kafka requires Topic and uses BootstrapServers for native publishing when PublishAsync is absent; an explicit PublishAsync delegate takes precedence. C# StatsD, DogStatsD, and Netdata use Host and Port for native UDP when SendLineAsync is absent. A completed UDP send means the local UDP stack accepted the datagram, not that any receiver acknowledged delivery.
Go Kafka Publisher
Go Kafka reporting uses the application-supplied `KafkaReportingSinkOptions.Publish` function; it is not a built-in Kafka client. The function receives the selected topic and serialized reporting payload, with `loadstrike-events` used when Topic is blank. If the function is missing, LoadStrike records the initialization error and disables that reporting sink before traffic; the workload can still run and its counts are unchanged. A returned error follows the shared retry policy and, after exhaustion, is recorded against and can disable the sink without changing the workload request, success, or failure counts.
Search Document Routing
.NET defaults Elasticsearch and OpenSearch IndexName to loadstrike-runs. Its default / EndpointPath signals automatic routing, so an unset path derives /{URL-escaped IndexName}/_doc; an explicitly supplied EndpointPath takes precedence as the compatibility override. Python and TypeScript or JavaScript use the same document route with a loadstrike-events default unless their explicit path override wins. Elasticsearch ApiKey and OpenSearch Username and Password produce the matching authorization header. Java gives the application publisher the sink name and JSON payload, so the application owns the document-ingestion target and delivery policy. Go requires a caller-supplied document-ingestion endpoint and leaves routing to the application.
Search Index Validation
For direct .NET, Python, and TypeScript or JavaScript routing, a selected IndexName must be nonblank lowercase text, at most 255 UTF-8 bytes, not . or .., and cannot start with -, _, or +. It rejects \, /, *, ?, ", <, >, |, ASCII space, comma, #, colon, control characters, and malformed Unicode. Diagnostics identify the affected option or full configuration path without echoing the rejected value. An explicit EndpointPath makes IndexName dormant, so the unused name is not validated.
Metric Value Limits
Every direct metric sink accepts only finite values in the inclusive range [-1e100, 1e100]. Limits are measured after provider-specific normalization and before encoding, except for the documented Dynatrace minimum-length prefix step. Ill-formed Unicode, reserved-key overrides, and key collisions after normalization fail before network I/O. The reserved scenario and status attributes consume two of 20 slots, leaving 18 for user attributes; evidence-only run_id leaves 17. Authoritative scenario and status values may not be empty. Prometheus also reserves __name__.
Prometheus Limits
Metric names are 1-200 UTF-8 bytes and normalize to [A-Za-z_:][A-Za-z0-9_:]*. A point has at most 20 labels. Label keys are 1-64 UTF-8 bytes and normalize to [A-Za-z_][A-Za-z0-9_]*; values are at most 256 UTF-8 bytes and may be empty.
CloudWatch Limits
Namespaces are 1-100 UTF-8 bytes and may not start with a colon. Metric names are 1-200 UTF-8 bytes. A point has at most 20 dimensions. Keys are 1-64 printable ASCII bytes, nonblank, and may not start with a colon; values are 1-256 printable ASCII bytes and nonblank. Count metrics always use Count. Gauge aliases map count or counts to Count, ms or millisecond or milliseconds to Milliseconds, byte or bytes to Bytes, and s or sec or second or seconds to Seconds; every other or unitless gauge uses None.
Dynatrace And New Relic Limits
Dynatrace metric names are 1-200 UTF-8 bytes before deterministic provider-valid minimum-length prefixing; up to 20 dimensions use 1-64 UTF-8-byte keys and 1-255 UTF-8-byte values. New Relic metric names are 1-200 UTF-8 bytes; up to 20 attributes use 1-64 UTF-8-byte keys normalized to [A-Za-z0-9:._]+ and values of at most 256 UTF-8 bytes, including empty values.
Canonical Infra Keys
Canonical sections are PrometheusRemoteWrite, CloudWatch, Dynatrace, Elasticsearch, OpenSearch, NewRelic, Webhook, Kafka, StatsD, Netdata, DogStatsD, and Jsonl. HTTP sinks use BaseUrl, EndpointPath, TimeoutSeconds, Headers, and StaticTags where supported. Provider fields are Prometheus BearerToken; CloudWatch EndpointUrl, Namespace, Region, AccessKeyId, SecretAccessKey, and SessionToken; Dynatrace ApiToken; Elasticsearch IndexName and ApiKey; OpenSearch IndexName, Username, and Password; New Relic LicenseKey or ApiKey; Webhook Url and Secret; Kafka BootstrapServers, Topic, and StaticTags; StatsD-family Prefix, Host, Port, and the documented Tags or StaticTags aliases; and JSONL FilePath, called Path in Java constructor options. Only DogStatsD serializes tag maps.
Expanded Config And Redaction
Selected LoadStrike:ReportingSinks:* sections fill missing values only, while explicit code values win and dormant sections are ignored. Canonical keys cover HTTP endpoint, timeout, headers, tags, provider credentials, namespace and region, Kafka bootstrap servers and topic, StatsD host and port, and JSONL file path. Unknown keys preserve their full configuration path. Diagnostics redact API keys, access keys, secrets, tokens, passwords, and sensitive authorization headers.
Generic Webhook Migration
GenericWebhook sends LoadStrike reporting JSON, including gzip-compressed observation batches where applicable, without converting the body to a vendor metric protocol. Migrate an old generic metrics URL to GenericWebhook only when its receiver accepts this generic body; otherwise use the matching direct provider sink.
Failure Behavior
Sink initialization, realtime export, raw delivery, completion, final export, stop, and dispose all use the configured bounded retry policy and remain isolated from scenario execution. A recovered callback adds no final sink error. Persistent sink failures are recorded in the run result and can disable that sink for the current run, while the load test itself continues. Stop and dispose are best-effort cleanup, and an exhausted stop callback does not prevent dispose.
Plan Gate
Expanded built-in reporting sinks are available on Enterprise. The same runner key validates the scenario, the selected sinks, portal reporting, and any transport features used by the run.
Expanded sink setup
Select each destination in code, then provide its C# connection settings through options or the matching infra-config section. Configuration never enables an unselected sink, and code-supplied values take precedence.
If you run these examples locally, add a valid runner key before execution starts. Set it with WithRunnerKey("...") or the config key LoadStrike:RunnerKey.
HTML reports include a top-right Light/Dark theme toggle and responsive offline SVG charts. Use exact-value tooltips, legends, zoom, pan, reset, the accessible expanded view, chart-title search, and grid-density controls to inspect dense results. Light is the default report theme.
Expanded Built-In Sinks
using LoadStrike;
var scenario = LoadStrikeScenario.Empty("orders-expanded-sinks")
.WithLoadSimulations(LoadStrikeSimulation.Inject(20, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(120)));
var prometheus = new PrometheusRemoteWriteReportingSink(new PrometheusRemoteWriteReportingSinkOptions
{
BaseUrl = "https://prometheus.example.com",
BearerToken = Environment.GetEnvironmentVariable("PROMETHEUS_BEARER_TOKEN") ?? string.Empty
});
var jsonl = new JsonlFileReportingSink(new JsonlFileReportingSinkOptions
{
FilePath = "./reports/loadstrike-events.jsonl"
});
// Use GenericWebhook only when the receiver accepts generic LoadStrike JSON.
var webhook = new GenericWebhookReportingSink(new GenericWebhookReportingSinkOptions
{
Url = "https://loadstrike-gateway.example.com/ingest",
Secret = "gateway-secret"
});
LoadStrikeRunner.RegisterScenarios(scenario)
.WithReportingInterval(TimeSpan.FromSeconds(5))
.WithReportingSinks(prometheus, jsonl, webhook)
.WithRunnerKey("rkl_your_enterprise_runner_key")
.Run();
package main
import loadstrike "loadstrike.com/sdk/go"
func main() {
scenario := loadstrike.EmptyScenario("orders-expanded-sinks").
WithLoadSimulations(loadstrike.LoadStrikeSimulation.Inject(20, loadstrike.DurationFromSeconds(1), loadstrike.DurationFromSeconds(120)))
context := loadstrike.RegisterScenarios(scenario).
WithReportingInterval(loadstrike.DurationFromSeconds(5)).
WithReportingSinks(
loadstrike.PrometheusRemoteWriteReportingSink{
Options: loadstrike.HTTPReportingSinkOptions{EndpointURL: "https://prometheus.example.com/api/v1/write"},
},
loadstrike.JsonlFileReportingSink{
Options: loadstrike.JsonlFileReportingSinkOptions{Path: "./reports/loadstrike-events.jsonl"},
},
// Use GenericWebhook only when the receiver accepts generic LoadStrike JSON.
loadstrike.GenericWebhookReportingSink{
Options: loadstrike.HTTPReportingSinkOptions{EndpointURL: "https://loadstrike-gateway.example.com/ingest"},
},
).
WithRunnerKey("rkl_your_enterprise_runner_key")
_ = context
}
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
import java.time.Duration;
import com.loadstrike.runtime.LoadStrikeRuntime.LoadStrikeRunner;
import com.loadstrike.runtime.LoadStrikeRuntime.LoadStrikeScenario;
import com.loadstrike.runtime.LoadStrikeRuntime.LoadStrikeSimulation;
import com.loadstrike.runtime.LoadStrikeSinks.JsonlFileReportingSink;
import com.loadstrike.runtime.LoadStrikeSinks.PrometheusOptions;
import com.loadstrike.runtime.LoadStrikeSinks.PrometheusRemoteWriteReportingSink;
import com.loadstrike.runtime.LoadStrikeSinks.ProtocolPublishException;
import com.loadstrike.runtime.LoadStrikeSinks.ProtocolPublisher;
var scenario = LoadStrikeScenario.empty("orders-expanded-sinks")
.withLoadSimulations(LoadStrikeSimulation.inject(20, 1d, 120d));
var http = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
// Migration: direct metric sinks now require options plus a publisher.
ProtocolPublisher publisher = (target, headers, body) -> {
var request = HttpRequest.newBuilder(URI.create(target))
.timeout(Duration.ofSeconds(30))
.POST(HttpRequest.BodyPublishers.ofByteArray(body));
headers.forEach(request::header);
try {
var response = http.send(request.build(), HttpResponse.BodyHandlers.discarding());
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new ProtocolPublishException(
response.statusCode(), "Reporting destination rejected the request.");
}
} catch (InterruptedException error) {
Thread.currentThread().interrupt();
throw new IllegalStateException("Reporting publish was interrupted.", error);
} catch (IOException error) {
throw new IllegalStateException("Reporting publish failed.", error);
}
};
var prometheus = new PrometheusRemoteWriteReportingSink(
new PrometheusOptions(
"https://prometheus.example.com/api/v1/write",
System.getenv("PROMETHEUS_BEARER_TOKEN")),
publisher);
var jsonl = new JsonlFileReportingSink(
Path.of("./reports/loadstrike-events.jsonl"));
LoadStrikeRunner.registerScenarios(scenario)
.withReportingInterval(5d)
.withReportingSinks(prometheus, jsonl)
.withRunnerKey("rkl_your_enterprise_runner_key")
.run();
import os
from loadstrike_sdk import (
GenericWebhookReportingSink,
JsonlFileReportingSink,
LoadStrikeRunner,
LoadStrikeScenario,
LoadStrikeSimulation,
PrometheusRemoteWriteReportingSink,
)
scenario = (
LoadStrikeScenario.empty("orders-expanded-sinks")
.with_load_simulations(LoadStrikeSimulation.inject(20, 1, 120))
)
jsonl = JsonlFileReportingSink(file_path="./reports/loadstrike-events.jsonl")
prometheus = PrometheusRemoteWriteReportingSink(
base_url="https://prometheus.example.com",
bearer_token=os.environ.get("PROMETHEUS_BEARER_TOKEN", ""),
)
# Migration from an old generic metrics URL: this body remains LoadStrike JSON.
webhook = GenericWebhookReportingSink(
base_url="https://loadstrike-gateway.example.com",
endpoint_path="/ingest",
headers={"Authorization": "LoadStrike gateway-secret"},
)
LoadStrikeRunner.register_scenarios(scenario) \
.with_reporting_interval(5) \
.with_reporting_sinks(prometheus, jsonl, webhook) \
.with_runner_key("rkl_your_enterprise_runner_key") \
.run()
import {
GenericWebhookReportingSink,
JsonlFileReportingSink,
LoadStrikeRunner,
LoadStrikeScenario,
LoadStrikeSimulation,
PrometheusRemoteWriteReportingSink
} from "@loadstrike/loadstrike-sdk";
const scenario = LoadStrikeScenario
.empty("orders-expanded-sinks")
.withLoadSimulations(LoadStrikeSimulation.inject(20, 1, 120));
const jsonl = new JsonlFileReportingSink({ FilePath: "./reports/loadstrike-events.jsonl" });
const prometheus = new PrometheusRemoteWriteReportingSink({
BaseUrl: "https://prometheus.example.com",
BearerToken: process.env.PROMETHEUS_BEARER_TOKEN
});
// Migration from an old generic metrics URL: this body remains LoadStrike JSON.
const webhook = new GenericWebhookReportingSink({
BaseUrl: "https://loadstrike-gateway.example.com",
EndpointPath: "/ingest",
Headers: { Authorization: "LoadStrike gateway-secret" }
});
await LoadStrikeRunner
.registerScenarios(scenario)
.withReportingInterval(5)
.withReportingSinks(prometheus, jsonl, webhook)
.withRunnerKey("rkl_your_enterprise_runner_key")
.run();
const {
GenericWebhookReportingSink,
JsonlFileReportingSink,
LoadStrikeRunner,
LoadStrikeScenario,
LoadStrikeSimulation,
PrometheusRemoteWriteReportingSink
} = require("@loadstrike/loadstrike-sdk");
(async () => {
const scenario = LoadStrikeScenario
.empty("orders-expanded-sinks")
.withLoadSimulations(LoadStrikeSimulation.inject(20, 1, 120));
const jsonl = new JsonlFileReportingSink({ FilePath: "./reports/loadstrike-events.jsonl" });
const prometheus = new PrometheusRemoteWriteReportingSink({
BaseUrl: "https://prometheus.example.com",
BearerToken: process.env.PROMETHEUS_BEARER_TOKEN
});
// Migration from an old generic metrics URL: this body remains LoadStrike JSON.
const webhook = new GenericWebhookReportingSink({
BaseUrl: "https://loadstrike-gateway.example.com",
EndpointPath: "/ingest",
Headers: { Authorization: "LoadStrike gateway-secret" }
});
await LoadStrikeRunner
.registerScenarios(scenario)
.withReportingInterval(5)
.withReportingSinks(prometheus, jsonl, webhook)
.withRunnerKey("rkl_your_enterprise_runner_key")
.run();
})();
C# expanded reporting sink configuration fields
Selects the infra-config section read by the registered sink. Each sink defaults to its LoadStrike:ReportingSinks:<SinkName> section.
HTTP sinks use BaseUrl, EndpointPath, TimeoutSeconds, Headers, and StaticTags where supported. Provider fields are Prometheus BearerToken; CloudWatch EndpointUrl, Namespace, Region, AccessKeyId, SecretAccessKey, and SessionToken; Dynatrace ApiToken; Elasticsearch IndexName and ApiKey; OpenSearch IndexName, Username, and Password; New Relic LicenseKey or ApiKey; Webhook Url and Secret; Kafka BootstrapServers, Topic, and StaticTags; StatsD-family Prefix, Host, Port, and the documented Tags or StaticTags aliases; and JSONL FilePath (Path in Java constructor options). Only DogStatsD serializes tag maps.
HTTP destination and path fields. A configured BaseUrl cannot contain a query or fragment; EndpointPath must be relative and path-like, may contain a query, and cannot contain a fragment. GenericWebhook accepts Url as its complete endpoint, permits its query, rejects its fragment, and ignores EndpointPath when Url wins. CloudWatch retains a non-root EndpointPath when BaseUrl is also supplied.
Common HTTP timeout and request-header settings. Configure Headers as a nested key/value map with valid HTTP names; content-managed and destination-managed headers cannot be overridden.
Tags for supported event and metric exports. Elasticsearch, OpenSearch, and Webhook add them to ordinary event envelopes and projected metrics without rewriting canonical iteration-batch or completion payloads.
Destination-specific credentials for Prometheus remote write, Dynatrace, Elasticsearch, New Relic, and Webhook. Protected configuration providers are recommended for secret values.
Required CloudWatch metric namespace, AWS region, and credentials.
Target index used for reporting events. OpenSearch Username and Password must be configured together when basic authentication is used.
.NET sends one JSON document per reporting call, defaults IndexName to loadstrike-runs, and treats its default / EndpointPath as the automatic-routing signal: an unset path derives /{URL-escaped IndexName}/_doc, while an explicitly supplied EndpointPath takes precedence. Python and TypeScript or JavaScript use the same document route with a loadstrike-events default unless their explicit path override wins. Java passes the sink name and JSON payload to an application publisher, while Go requires a caller-supplied document-ingestion endpoint.
For direct .NET, Python, and TypeScript or JavaScript routing, a selected IndexName must be nonblank lowercase text, at most 255 UTF-8 bytes, not . or .., and cannot start with -, _, or +. It rejects \, /, *, ?, ", <, >, |, ASCII space, comma, #, colon, control characters, and malformed Unicode. Diagnostics identify the affected option or full configuration path without echoing the rejected value. An explicit EndpointPath makes IndexName dormant, so the unused name is not validated.
Prometheus Remote Write, CloudWatch, Dynatrace, and New Relic use provider-specific metric protocols in every SDK. Java supplies provider options and a ProtocolPublisher that transmits the target, headers, and bytes and owns retry, TLS, and proxy policy. Python Prometheus and CloudWatch require the reporting extra.
GenericWebhook sends generic LoadStrike reporting JSON and does not translate it into a vendor metric protocol.
C# uses BootstrapServers for native publishing when PublishAsync is absent, and an explicit PublishAsync delegate takes precedence. Go uses an application Publish(topic, payload) function, defaults a blank Topic to loadstrike-events, and does not treat that function as a native Kafka client. If Publish is missing, LoadStrike records the initialization error and disables that reporting sink before traffic; the workload can still run and its counts are unchanged. Go publisher errors use sink retries and, after exhaustion, are recorded against the sink without changing workload counts.
Metric prefix and native UDP destination for StatsD-family sinks. Prefix shapes all emitted lines. IPv4, IPv6, and DNS hosts are supported; keep the port in the separate Port field. A completed UDP send means the local UDP stack accepted the datagram, not that a receiver acknowledged it.
DogStatsD serializes both maps as tag suffixes, with StaticTags applied after Tags. Plain StatsD and Netdata do not serialize tag maps. Configured values reject line-protocol delimiters, and explicit code tag values take precedence over configured aliases.
Optional code-only callback for StatsD, DogStatsD, and Netdata. When supplied, Host and Port are dormant.
Required destination file path for newline-delimited JSON reporting envelopes.
Explicit option values win over infra config, including explicit defaults, empty values, and empty maps. Unselected sink sections remain dormant.
{
"LoadStrike": {
"ReportingSinks": {
"PrometheusRemoteWrite": {
"BaseUrl": "https://prometheus.example.com",
"BearerToken": "prometheus-token"
},
"CloudWatch": {
"Namespace": "LoadStrike/ProductionLike",
"Region": "eu-west-2",
"AccessKeyId": "access-key-id",
"SecretAccessKey": "secret-access-key"
},
"Dynatrace": {
"BaseUrl": "https://example.live.dynatrace.com",
"ApiToken": "dynatrace-token"
},
"Elasticsearch": {
"BaseUrl": "https://elastic.example.com",
"IndexName": "loadstrike-runs",
"ApiKey": "elastic-api-key"
},
"OpenSearch": {
"BaseUrl": "https://opensearch.example.com",
"IndexName": "loadstrike-runs",
"Username": "loadstrike",
"Password": "opensearch-password"
},
"NewRelic": {
"BaseUrl": "https://metric-api.newrelic.com",
"LicenseKey": "new-relic-license-key"
},
"Webhook": {
"Url": "https://loadstrike-gateway.example.com/ingest",
"Secret": "gateway-secret",
"Headers": { "X-Tenant": "payments" }
},
"Kafka": {
"BootstrapServers": "broker-1.example.com:9092,broker-2.example.com:9092",
"Topic": "loadstrike-runs"
},
"StatsD": {
"Host": "statsd.example.com",
"Port": 8125,
"Prefix": "loadstrike"
},
"DogStatsD": {
"Host": "dogstatsd.example.com",
"Port": 8125,
"Tags": { "service": "checkout" },
"StaticTags": { "environment": "production-like" }
},
"Netdata": {
"Host": "netdata.example.com",
"Port": 8125
},
"Jsonl": {
"FilePath": "./reports/loadstrike-events.jsonl"
}
}
}
}
Callbacks and injected clients are supplied in code and are never read from infra config. Binding does not mutate caller-owned options; those options may be reused when the application treats them as immutable while runs are active.