Reporting Sinks

Reporting sinks send LoadStrike data to the customer portal or supported observability backends for dashboards, alerts, search, and longer-term review.

What this page helps you do

What this page helps you do

Reporting sinks send LoadStrike data to the customer portal or supported observability backends for dashboards, alerts, search, and longer-term review.

Who this is for

Engineers writing or reviewing scenario code in one of the supported SDKs.

Prerequisites

  • A scenario or runtime surface you want to wire correctly in code

By the end

The exact SDK surface you need for this part of the runtime.

Use this page when

Use this reference when you already know the workflow and need the exact Reporting Sinks API surface in code.

Visual guide

Reporting sink flow showing runtime events moving to external observability backends.
Reporting sinks receive realtime snapshots and final run metadata without changing the scenario workload.

Guide

Built-In Vendor Sinks

LoadStrike ships first-party sinks for InfluxDB, TimescaleDB, Grafana Loki, Datadog, Splunk HEC, OTEL Collector, Prometheus remote write, Amazon CloudWatch, Dynatrace, Elasticsearch, OpenSearch, Kafka, StatsD, DogStatsD, New Relic, Netdata-compatible StatsD, JSONL file streaming, and generic webhooks. Event-capable destinations receive bounded batches of individual attempt observations as well as reporting events and metric projections. Metric-only destinations receive the closest supported per-attempt metric projection and report a shape-limited warning when they cannot retain the complete observation envelope. Final export also includes started and completed timestamps plus run-result metadata such as report files, disabled sink names, and sink error counts.

Customer Portal Reports

Use WithPortalReporting when a run should appear in the customer portal Run Reports tab. Portal reports are meant for account-level review: parallel and repeated runs are listed separately with their run timestamp, and users can plot one or more scenarios over 1D, 1W, 15D, 30D, or a custom period. Users can also choose a run to graph the historical metrics for all scenarios in that run, write focused report queries, save recurring views, export visible trends, share filtered links, and open scenario detail views.

High-Volume Counters

Run, scenario, measurement, latency-band, and status results expose authoritative decimal-string Count64 fields for high-volume tests. Existing 32-bit count properties remain available for compatibility and stop at 2,147,483,647 instead of wrapping. Use the Count64 values when exporting or calculating totals that could exceed that range.

Configuration Model

Each sink accepts an options object in code and can also fill missing values from infra config sections such as LoadStrike:ReportingSinks:InfluxDb, LoadStrike:ReportingSinks:TimescaleDb, LoadStrike:ReportingSinks:GrafanaLoki, LoadStrike:ReportingSinks:Datadog, LoadStrike:ReportingSinks:Splunk, and LoadStrike:ReportingSinks:OtelCollector. TypeScript and JavaScript also expose named option constructors alongside object-literal inputs. Influx can split projected metrics into MetricsMeasurementName, TimescaleDB can split them into MetricsTableName, Grafana Loki can keep logs in Loki while sending OTLP/HTTP metrics through MetricsBaseUrl, MetricsEndpointPath, and MetricsHeaders, Splunk uses the HEC event endpoint, OTEL uses OTLP/HTTP, and final detailed report rows are included in exported sink data where supported.

Sink Identity

Each sink exposes a stable SinkName or equivalent sink identifier. LoadStrike uses that name for registration, licensing checks, disabled sink tracking, and sinkErrors output, so choose a descriptive name that stays stable across runs when you implement a custom sink.

Raw Attempt Batches

Each scenario attempt, including retries and nested step timings, is captured as an individual observation. LoadStrike normally groups those observations for up to five seconds and limits a batch to 50,000 observations or 8 MiB before sending it. Finite reported latencies above the signed 64-bit microsecond range are retained at the maximum representable value. Request and message payloads, bodies, and headers are not included. Percentiles are calculated later by the receiving platform from the observations instead of being pre-aggregated by the SDK.

Retry Before Observation Loss

Every reporting-sink callback, from initialization and start through realtime statistics and metrics, final statistics and metrics, raw batches, completion, stop, and dispose, is attempted once and then receives up to three retries by default. The retry delays are 250 ms, 500 ms, and 1 second, and SinkRetryCount can select zero through 100 retries. A recovered callback adds no final sink error or delivery warning. Only an exhausted raw-observation delivery counts as dropped observations; other exhausted callbacks are reported against that sink without becoming a system-under-test failure. SinkRetryBackoffMs changes the first delay. Stop and dispose remain best-effort cleanup, and an exhausted stop callback does not prevent dispose. Exception details remain in sanitized local logs rather than portable reports. Load generation remains the priority, so an oversized observation or a full bounded buffer or sink queue is not retried and does not become a system-under-test failure.

Export Ordering

Realtime sink callbacks happen during execution. When the run ends, LoadStrike builds the final LoadStrikeRunResult artifact, writes any local reports that are still enabled, sends SaveRunResult to the sink with that finished artifact, and then calls Stop followed by Dispose. That ordering means final sink export sees completed threshold, detailed report, report-file, disabled-sink, and sink-error data.

Grafana Starter Assets

The documentation downloads area includes datasource YAML files, a shared dashboard-provider YAML file, and starter dashboard JSON files so teams can wire Grafana to Loki, InfluxDB, or TimescaleDB without pulling the source repository.

Custom Sink Contract

If you need a destination that LoadStrike does not ship out of the box, implement the reporting-sink contract for your SDK and register it through WithReportingSinks, withReportingSinks, or with_reporting_sinks. The shared lifecycle is Init, Start, SaveRealtimeStats, SaveRealtimeMetrics, SaveRunResult, Stop, and Dispose. To retain every attempt, also implement the optional iteration-batch capability for that SDK; it receives bounded raw batches during the run and one completion record with captured, delivered, and dropped counts. SaveRunResult receives the full run artifact, including report file paths, disabled sink names, sink error details, correlation rows, scenario durations, and started and completed timestamps.

Plan Gate

Portal reporting, built-in vendor sinks, and custom reporting sinks are available on Enterprise.

SDK reference samples

Use these SDK samples to compare how Reporting Sinks is exposed across the supported languages before you wire it into a full scenario.

If you run these examples locally, add a valid runner key before execution starts. Set it with WithRunnerKey("...") or the config key LoadStrike:RunnerKey.

Reporting Sink

using LoadStrike;

var scenario = LoadStrikeScenario
    .Create("submit-orders", _ => Task.FromResult(LoadStrikeResponse.Ok(statusCode: "200")))
    .WithLoadSimulations(LoadStrikeSimulation.Inject(10, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(20)));

var sink = new DatadogReportingSink(new DatadogReportingSinkOptions
{
    BaseUrl = "https://api.datadoghq.com",
    ApiKey = "dd-api-key",
    ApplicationKey = "dd-app-key",
    StaticTags =
    {
        ["environment"] = "staging"
    },
    StaticAttributes =
    {
        ["team"] = "perf"
    }
});

LoadStrikeRunner.RegisterScenarios(scenario)
    .WithReportingSinks(sink)
    .WithRunnerKey("rkl_your_local_runner_key")
    .Run();

Built-In Sink Options

WithPortalReporting()

Sends run summaries and scenario trends to the LoadStrike customer portal Run Reports tab for Enterprise accounts.

InfluxDb

Writes reporting events and metric projections into InfluxDB, with an optional separate metrics measurement.

TimescaleDb

Writes reporting events and projected metrics into PostgreSQL or TimescaleDB tables.

GrafanaLoki

Writes log-style reporting events to Loki and can forward projected metrics through the OTLP metrics companion settings.

Datadog

Sends reporting events to Datadog logs and projected metrics to Datadog metrics.

Splunk

Sends both reporting events and metric projections through the Splunk HEC event endpoint.

OtelCollector

Sends OTLP/HTTP logs and metrics to an OpenTelemetry collector pipeline.

Expanded sinks

Prometheus remote write, CloudWatch, Dynatrace, Elasticsearch, OpenSearch, Kafka, StatsD, DogStatsD, New Relic, Netdata, JSONL, and generic webhook sinks are covered in the expanded built-in sinks page.

SinkName / sink identity

Every sink uses a stable sink identifier so disabledSinks and sinkErrors can point to the correct destination during troubleshooting.

Raw attempt batches

Event-capable sinks receive individual attempts and nested step timings in batches that normally flush after five seconds, 50,000 observations, or 8 MiB. A Fail-mode runtime policy callback that fails after an attempt begins produces one final sanitized runtime_policy_error observation before the run stops. Finite reported latencies above the signed 64-bit microsecond range are retained at the maximum representable value. Payloads, bodies, headers, and callback error text are excluded, and the destination calculates percentiles from the observations.

High-volume counters

Use the authoritative decimal-string Count64 fields when a run, scenario, measurement, latency band, or status total can exceed 2,147,483,647. Legacy integer count properties remain compatible and cap at that value instead of wrapping.

Retry before loss

Every reporting-sink callback is attempted once, then receives three retries by default after 250 ms, 500 ms, and 1 second. Recovered callbacks add no final sink error or warning. Only exhausted raw-observation delivery counts as dropped observations; other exhausted callbacks are reported against that sink. Stop and dispose remain best-effort cleanup.

Local diagnostics

Exception details remain in sanitized local logs rather than portable reports. Oversized observations and full bounded buffers or sink queues are not retried, and reporting pressure never becomes a synthetic system-under-test failure.

Final export ordering

SaveRunResult receives the completed LoadStrikeRunResult before Stop and Dispose run, so threshold rows, plugin-produced report sections, report files, and sink error metadata are already available.

Custom sink

Enterprise projects can also register a custom reporting sink implementation when a built-in destination is not enough.

{
  "LoadStrike": {
    "SinkRetryCount": 3,
    "SinkRetryBackoffMs": 250,
    "ReportingSinks": {
      "InfluxDb": {
        "BaseUrl": "https://influx.example.com",
        "Organization": "performance-team",
        "Bucket": "loadstrike-runs",
        "Token": "influx-token",
        "MeasurementName": "loadstrike_events",
        "MetricsMeasurementName": "loadstrike_metrics"
      },
      "TimescaleDb": {
        "ConnectionString": "Host=db.example.com;Port=5432;Database=loadstrike;Username=postgres;Password=postgres",
        "Schema": "observability",
        "TableName": "loadstrike_reporting_events",
        "MetricsTableName": "loadstrike_reporting_metrics"
      },
      "GrafanaLoki": {
        "BaseUrl": "https://loki.example.com",
        "BearerToken": "loki-token",
        "MetricsBaseUrl": "https://otel-gateway.example.com",
        "MetricsEndpointPath": "/v1/metrics",
        "MetricsHeaders": {
          "Authorization": "Bearer otlp-metrics-token"
        }
      },
      "Datadog": {
        "BaseUrl": "https://api.datadoghq.com",
        "ApiKey": "dd-api-key",
        "ApplicationKey": "dd-app-key"
      },
      "Splunk": {
        "BaseUrl": "https://splunk.example.com",
        "Token": "splunk-hec-token"
      },
      "OtelCollector": {
        "BaseUrl": "https://otel.example.com",
        "Headers": {
          "Authorization": "Bearer otel-token"
        }
      },
      "PrometheusRemoteWrite": {
        "BaseUrl": "https://prometheus.example.com",
        "EndpointPath": "/api/v1/write"
      },
      "CloudWatch": {
        "Region": "eu-west-2",
        "Namespace": "LoadStrike"
      },
      "Dynatrace": {
        "BaseUrl": "https://dynatrace.example.com"
      },
      "Elasticsearch": {
        "BaseUrl": "https://elastic.example.com",
        "IndexName": "loadstrike-runs"
      },
      "OpenSearch": {
        "BaseUrl": "https://opensearch.example.com",
        "IndexName": "loadstrike-runs"
      },
      "Kafka": {
        "BootstrapServers": "kafka.example.com:9092",
        "Topic": "loadstrike-reporting"
      },
      "StatsD": {
        "Prefix": "loadstrike"
      },
      "DogStatsD": {
        "Prefix": "loadstrike"
      },
      "NewRelic": {
        "BaseUrl": "https://metric-api.newrelic.com"
      },
      "Netdata": {
        "Prefix": "loadstrike"
      },
      "Jsonl": {
        "FilePath": "./reports/loadstrike-events.jsonl"
      },
      "Webhook": {
        "Url": "https://hooks.example.com/loadstrike"
      }
    }
  }
}

Individual Sink Samples

Pick Portal Reporting when users should review runs inside the customer portal, or choose the sink that matches the backend your team already uses. Event-capable built-in sinks send bounded batches of individual attempts plus reporting events and metric projections while the run is active; metric-only sinks expose the closest supported projection and disclose the reduced shape. Each completed correlation outcome is exported as an individual final event with its tracking and GatherBy dimensions, while the destination remains responsible for percentile aggregation. The final export also includes observation delivery counts, metric snapshots, timestamps, report-file metadata, disabled sink names, sink errors, and failed responses. If you load infra config, missing values can bind from LoadStrike:ReportingSinks:* sections.

Portal Reporting

Choose Portal Reporting when Enterprise users should review run history, trends, percentiles, and bytes from the customer portal.

var scenario = LoadStrikeScenario.Empty("orders-portal-reporting")
    .WithLoadSimulations(
        LoadStrikeSimulation.Inject(rate: 25, interval: TimeSpan.FromSeconds(1), during: TimeSpan.FromMinutes(2))
    );

LoadStrikeRunner.RegisterScenarios(scenario)
    .WithReportingInterval(TimeSpan.FromSeconds(5))
    .WithPortalReporting()
    .WithRunnerKey("rkl_your_enterprise_runner_key")
    .Run();

Datadog Sink

Choose Datadog when your team already reads events in Datadog Logs and metrics in Datadog Metrics.

var scenario = LoadStrikeScenario.Empty("orders-load")
    .WithLoadSimulations(
        LoadStrikeSimulation.Inject(rate: 25, interval: TimeSpan.FromSeconds(1), during: TimeSpan.FromMinutes(2))
    );

var datadogSink = new DatadogReportingSink(new DatadogReportingSinkOptions
{
    BaseUrl = "https://api.datadoghq.com",
    ApiKey = "dd-api-key",
    ApplicationKey = "dd-app-key",
    StaticTags =
    {
        ["environment"] = "staging",
        ["service"] = "orders-api"
    },
    StaticAttributes =
    {
        ["team"] = "performance"
    }
});

LoadStrikeRunner.RegisterScenarios(scenario)
    .WithReportingSinks(datadogSink)
    .WithRunnerKey("rkl_your_local_runner_key")
    .Run();

Splunk HEC Sink

Choose Splunk when your team already uses the HEC event endpoint and wants both reporting events and metric projections there.

var scenario = LoadStrikeScenario.Empty("checkout-load")
    .WithLoadSimulations(
        LoadStrikeSimulation.KeepConstant(copies: 12, during: TimeSpan.FromMinutes(5))
    );

var splunkSink = new SplunkReportingSink(new SplunkReportingSinkOptions
{
    BaseUrl = "https://splunk.example.com",
    Token = "splunk-hec-token",
    Source = "loadstrike",
    Sourcetype = "_json",
    Index = "observability",
    StaticFields =
    {
        ["environment"] = "preprod",
        ["service"] = "checkout"
    }
});

LoadStrikeRunner.RegisterScenarios(scenario)
    .WithReportingSinks(splunkSink)
    .WithRunnerKey("rkl_your_local_runner_key")
    .Run();

OTEL Collector Sink

Choose OTEL Collector when your team already sends telemetry through an OpenTelemetry collector or a compatible OTLP/HTTP pipeline.

var scenario = LoadStrikeScenario.Empty("payments-load")
    .WithLoadSimulations(
        LoadStrikeSimulation.Inject(rate: 10, interval: TimeSpan.FromSeconds(1), during: TimeSpan.FromMinutes(3))
    );

var otelSink = new OtelCollectorReportingSink(new OtelCollectorReportingSinkOptions
{
    BaseUrl = "https://otel.example.com",
    Headers =
    {
        ["Authorization"] = "Bearer otel-token"
    },
    StaticResourceAttributes =
    {
        ["deployment.environment"] = "production-like",
        ["service.name"] = "payments"
    }
});

LoadStrikeRunner.RegisterScenarios(scenario)
    .WithReportingSinks(otelSink)
    .WithRunnerKey("rkl_your_local_runner_key")
    .Run();

InfluxDB Sink

Choose InfluxDB when you want every raw attempt and nested step written as a separate point alongside events and projected metrics. Use MetricsMeasurementName if projected metrics should live in a separate measurement.

var scenario = LoadStrikeScenario.Empty("orders-load")
    .WithLoadSimulations(
        LoadStrikeSimulation.Inject(rate: 25, interval: TimeSpan.FromSeconds(1), during: TimeSpan.FromMinutes(2))
    );

var influxSink = new InfluxDbReportingSink(new InfluxDbReportingSinkOptions
{
    BaseUrl = "https://influx.example.com",
    Organization = "performance-team",
    Bucket = "loadstrike-runs",
    Token = "influx-token",
    MeasurementName = "loadstrike_orders",
    MetricsMeasurementName = "loadstrike_orders_metrics",
    StaticTags =
    {
        ["environment"] = "staging",
        ["service"] = "orders-api"
    }
});

LoadStrikeRunner.RegisterScenarios(scenario)
    .WithReportingSinks(influxSink)
    .WithRunnerKey("rkl_your_local_runner_key")
    .Run();

TimescaleDB Sink

Choose TimescaleDB when you want every raw attempt and nested step written as a separate PostgreSQL-compatible row alongside reporting events and a separate table for projected metrics.

var scenario = LoadStrikeScenario.Empty("checkout-load")
    .WithLoadSimulations(
        LoadStrikeSimulation.KeepConstant(copies: 12, during: TimeSpan.FromMinutes(5))
    );

var timescaleSink = new TimescaleDbReportingSink(new TimescaleDbReportingSinkOptions
{
    ConnectionString = "Host=db.example.com;Port=5432;Database=loadstrike;Username=postgres;Password=postgres",
    Schema = "observability",
    TableName = "loadstrike_reporting_events",
    MetricsTableName = "loadstrike_reporting_metrics",
    CreateSchemaIfMissing = true,
    EnableHypertableIfAvailable = true,
    StaticTags =
    {
        ["environment"] = "preprod",
        ["service"] = "checkout"
    }
});

LoadStrikeRunner.RegisterScenarios(scenario)
    .WithReportingSinks(timescaleSink)
    .WithRunnerKey("rkl_your_local_runner_key")
    .Run();

Grafana Loki Sink

Choose Grafana Loki when you want log-style reporting events in Loki and projected metrics sent through a companion OTLP/HTTP path for dashboards and alerts.

var scenario = LoadStrikeScenario.Empty("payments-load")
    .WithLoadSimulations(
        LoadStrikeSimulation.Inject(rate: 10, interval: TimeSpan.FromSeconds(1), during: TimeSpan.FromMinutes(3))
    );

var lokiSink = new GrafanaLokiReportingSink(new GrafanaLokiReportingSinkOptions
{
    BaseUrl = "https://loki.example.com",
    MetricsBaseUrl = "https://otel-gateway.example.com",
    MetricsEndpointPath = "/v1/metrics",
    MetricsHeaders =
    {
        ["Authorization"] = "Bearer otlp-metrics-token"
    },
    TenantId = "platform-team",
    BearerToken = "loki-token",
    StaticLabels =
    {
        ["environment"] = "production-like",
        ["service"] = "payments"
    }
});

LoadStrikeRunner.RegisterScenarios(scenario)
    .WithReportingSinks(lokiSink)
    .WithRunnerKey("rkl_your_local_runner_key")
    .Run();

Expanded Built-In Sinks

Choose this page for Prometheus remote write, CloudWatch, Dynatrace, Elasticsearch, OpenSearch, Kafka, StatsD, DogStatsD, New Relic, Netdata, JSONL, and webhook examples.

LoadStrikeRunner.RegisterScenarios(scenario)
    .WithReportingSinks(
        new PrometheusRemoteWriteReportingSink(new PrometheusRemoteWriteReportingSinkOptions
        {
            BaseUrl = "https://prometheus.example.com"
        }),
        new JsonlFileReportingSink(new JsonlFileReportingSinkOptions { FilePath = "./reports/loadstrike-events.jsonl" }))
    .WithRunnerKey("rkl_your_enterprise_runner_key")
    .Run();

Full Observability Asset Guide

Use these public downloads when you want the matching Grafana files or sink templates without pulling them from the source repository.

Public download paths:
- /downloads/grafana/provisioning/datasources/loadstrike-loki.yaml
- /downloads/grafana/provisioning/datasources/loadstrike-influxdb.yaml
- /downloads/grafana/provisioning/datasources/loadstrike-timescaledb.yaml
- /downloads/grafana/provisioning/dashboards/loadstrike.yaml
- /downloads/grafana/dashboards/loadstrike-loki-overview.json
- /downloads/grafana/dashboards/loadstrike-influxdb-overview.json
- /downloads/grafana/dashboards/loadstrike-timescaledb-overview.json
- /downloads/observability/sinks/loadstrike-datadog-reporting-sink.json
- /downloads/observability/sinks/loadstrike-splunk-reporting-sink.json
- /downloads/observability/sinks/loadstrike-otel-collector-reporting-sink.json
- /downloads/observability/loadstrike-observability-assets-guide.md