Logger
LoadStrike writes runtime logs for you by default. Use this page when you want to understand that default behavior or replace it with your own logger setup.
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
LoadStrike writes runtime logs for you by default. Use this page when you want to understand that default behavior or replace it with your own logger setup.
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 Logger API surface in code.
Visual guide
Guide
Default Behavior
With no extra setup, LoadStrike writes normal runtime logs to the console and to one text file in the configured report folder. The default file name follows loadstrike-log-<yyyyMMdd_HHmmss>[-coordinator|-agent][-machine].txt, and clustered runs only add node role or machine suffixes when needed to keep files distinct.
Minimum Level
Use WithMinimumLogLevel, withMinimumLogLevel, or with_minimum_log_level when the run should filter out lower-level events. Supported public levels are Verbose, Debug, Information, Warning, Error, and Fatal, and JSON or CLI configuration can use the same names or numeric 0 through 5 tokens.
Custom Logger Configuration
Use WithLoggerConfig, withLoggerConfig, or with_logger_config when logs need to go somewhere else. In .NET the callback returns a Serilog LoggerConfiguration, while Java, Python, TypeScript, and JavaScript expose the same idea through their native logger callback shapes.
Replacement Rule
A custom logger configuration replaces the default public logger pipeline. If you still want a text log file after overriding the logger, add file output inside your custom logger configuration.
Console Metrics Snapshots
DisplayConsoleMetrics shows live request, ok, and fail counters separately from the logger when realtime metrics are enabled. Those snapshot lines stay independent from the normal logger pipeline, so they can still appear even after the logger configuration is replaced.
Cluster Behavior
Each node writes its own log file. Local dev clusters place child node logs under node-specific report folders, and the final LoadStrikeRunResult includes the collected log file paths in LogFiles or logFiles when the coordinator can observe them.
Language Differences
The core model stays the same across SDKs, but a few helpers and result shapes use language-specific names. Check these notes when you switch languages or compare samples.
Use WithLoggerConfig(() => new LoggerConfiguration(...)) when you want Serilog sinks, enrichers, or custom formatting.
Use the native logger callback surface for that SDK. The behavior is the same, but the callback returns a language-native logger object instead of a Serilog configuration.
SDK reference samples
Use these SDK samples to compare how Logger 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.
Logger
using LoadStrike;
using System;
using Serilog;
using Serilog.Events;
var scenario = LoadStrikeScenario
.Create("submit-orders", _ => Task.FromResult(LoadStrikeResponse.Ok(statusCode: "200")))
.WithLoadSimulations(LoadStrikeSimulation.Inject(rate: 10, interval: TimeSpan.FromSeconds(1), during: TimeSpan.FromSeconds(20)));
LoadStrikeRunner.RegisterScenarios(scenario)
.WithReportFolder("./reports")
.WithMinimumLogLevel(LogEventLevel.Information)
.DisplayConsoleMetrics(true)
.WithLoggerConfig(() => new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("./custom-logs/orders.log"))
.WithRunnerKey("rkl_your_local_runner_key")
.Run();
package main
import (
"time"
loadstrike "loadstrike.com/sdk/go"
)
func main() {
loadstrike.RegisterScenarios(loadstrike.Empty("logger-demo")).
WithMinimumLogLevel(loadstrike.LogEventLevelDebug).
WithReportingInterval(loadstrike.TimeSpan(5 * time.Second)).
WithLoggerConfig(loadstrike.LoggerConfigurationFactory(func() loadstrike.LoggerConfiguration {
return loadstrike.LoggerConfiguration{
"format": "json",
"target": "stdout",
}
})).
Run()
}
import com.loadstrike.runtime.LoadStrikeRuntime.LoadStrikeResponse;
import com.loadstrike.runtime.LoadStrikeRuntime.LoadStrikeLogLevel;
import com.loadstrike.runtime.LoadStrikeRuntime.LoadStrikeLogger;
import com.loadstrike.runtime.LoadStrikeRuntime.LoadStrikeRunner;
import com.loadstrike.runtime.LoadStrikeRuntime.LoadStrikeScenario;
import com.loadstrike.runtime.LoadStrikeRuntime.LoadStrikeSimulation;
import com.loadstrike.runtime.LoadStrikeRuntime.LoadStrikeStep;
import java.util.logging.Logger;
Logger appLogger = Logger.getLogger("loadstrike-demo");
var scenario = LoadStrikeScenario
.create("submit-orders", context -> LoadStrikeStep.run(
"POST /orders",
context,
() -> LoadStrikeResponse.ok("200")
).asReply())
.withLoadSimulations(LoadStrikeSimulation.inject(10, 1d, 20d));
LoadStrikeRunner.registerScenarios(scenario)
.withReportFolder("./reports")
.withMinimumLogLevel(LoadStrikeLogLevel.Warning)
.displayConsoleMetrics(true)
.withLoggerConfig(() -> new LoadStrikeLogger() {
@Override
public void info(String message) {
appLogger.info("[loadstrike] " + message);
}
@Override
public void warn(String message) {
appLogger.warning("[loadstrike] " + message);
}
@Override
public void error(String message) {
appLogger.severe("[loadstrike] " + message);
}
})
.withRunnerKey("rkl_your_local_runner_key")
.run();
from loadstrike_sdk import (
LoadStrikeResponse,
LoadStrikeRunner,
LoadStrikeScenario,
LoadStrikeSimulation,
LoadStrikeStep,
)
import logging
from pathlib import Path
app_logger = logging.getLogger("loadstrike-demo")
if not app_logger.handlers:
Path("./custom-logs").mkdir(parents=True, exist_ok=True)
file_handler = logging.FileHandler("./custom-logs/orders.log", encoding="utf-8")
console_handler = logging.StreamHandler()
formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
app_logger.addHandler(file_handler)
app_logger.addHandler(console_handler)
app_logger.setLevel(logging.INFO)
class ConsoleLogger:
def debug(self, message: str) -> None:
app_logger.debug(message)
def info(self, message: str) -> None:
app_logger.info(message)
def warn(self, message: str) -> None:
app_logger.warning(message)
def error(self, message: str) -> None:
app_logger.error(message)
scenario = (
LoadStrikeScenario.create(
"submit-orders",
lambda context: LoadStrikeStep.run(
"POST /orders",
context,
lambda: LoadStrikeResponse.ok("200"),
).as_reply(),
)
.with_load_simulations(LoadStrikeSimulation.inject(10, 1, 20))
)
LoadStrikeRunner.register_scenarios(scenario) \
.with_report_folder("./reports") \
.with_minimum_log_level("Warning") \
.display_console_metrics(True) \
.with_logger_config(lambda: ConsoleLogger()) \
.with_runner_key("rkl_your_local_runner_key") \
.run()
import {
LoadStrikeResponse,
LoadStrikeRunner,
LoadStrikeScenario,
LoadStrikeSimulation,
LoadStrikeStep
} from "@loadstrike/loadstrike-sdk";
import { appendFileSync, mkdirSync } from "node:fs";
mkdirSync("./custom-logs", { recursive: true });
const scenario = LoadStrikeScenario
.create("submit-orders", async (context) => {
const step = await LoadStrikeStep.run("POST /orders", context, async () =>
LoadStrikeResponse.ok("200")
);
return step.asReply();
})
.withLoadSimulations(LoadStrikeSimulation.inject(10, 1, 20));
await LoadStrikeRunner.registerScenarios(scenario)
.withReportFolder("./reports")
.withMinimumLogLevel("Warning")
.displayConsoleMetrics(true)
.withLoggerConfig(() => ({
debug: (message) => appendFileSync("./custom-logs/orders.log", `[DBG] ${message}\n`, "utf8"),
info: (message) => {
console.info(message);
appendFileSync("./custom-logs/orders.log", `[INF] ${message}\n`, "utf8");
},
warn: (message) => {
console.warn(message);
appendFileSync("./custom-logs/orders.log", `[WRN] ${message}\n`, "utf8");
},
error: (message) => {
console.error(message);
appendFileSync("./custom-logs/orders.log", `[ERR] ${message}\n`, "utf8");
}
}))
.withRunnerKey("rkl_your_local_runner_key")
.run();
const {
LoadStrikeResponse,
LoadStrikeRunner,
LoadStrikeScenario,
LoadStrikeSimulation,
LoadStrikeStep
} = require("@loadstrike/loadstrike-sdk");
const { appendFileSync, mkdirSync } = require("node:fs");
mkdirSync("./custom-logs", { recursive: true });
(async () => {
const scenario = LoadStrikeScenario
.create("submit-orders", async (context) => {
const step = await LoadStrikeStep.run("POST /orders", context, async () =>
LoadStrikeResponse.ok("200")
);
return step.asReply();
})
.withLoadSimulations(LoadStrikeSimulation.inject(10, 1, 20));
await LoadStrikeRunner.registerScenarios(scenario)
.withReportFolder("./reports")
.withMinimumLogLevel("Warning")
.displayConsoleMetrics(true)
.withLoggerConfig(() => ({
debug: (message) => appendFileSync("./custom-logs/orders.log", `[DBG] ${message}\n`, "utf8"),
info: (message) => {
console.info(message);
appendFileSync("./custom-logs/orders.log", `[INF] ${message}\n`, "utf8");
},
warn: (message) => {
console.warn(message);
appendFileSync("./custom-logs/orders.log", `[WRN] ${message}\n`, "utf8");
},
error: (message) => {
console.error(message);
appendFileSync("./custom-logs/orders.log", `[ERR] ${message}\n`, "utf8");
}
}))
.withRunnerKey("rkl_your_local_runner_key")
.run();
})();
Logger APIs
Most teams can start with the default logger. LoadStrike writes to the console and to one loadstrike-log-<yyyyMMdd_HHmmss>[-coordinator|-agent][-machine].txt file in the report folder, so custom logger setup is only needed when logs must go somewhere else.
By default the SDK writes runtime logs to console and to one text file inside the report folder. The final LoadStrikeRunResult also exposes those paths through LogFiles / logFiles.
Single-node runs use loadstrike-log-<yyyyMMdd_HHmmss>.txt in the report folder. Clustered runs can append coordinator, agent, and machine suffixes when that is needed to keep node files distinct.
Sets the minimum runtime log level so lower-priority events are filtered before the logger writes them. Verbose, Debug, Information, Warning, Error, and Fatal are supported.
Replaces the default public logger pipeline with your project-specific logger. .NET expects a Serilog LoggerConfiguration callback; Java, Python, TypeScript, and JavaScript expect native logger factories or callback objects.
Custom logger configuration does not layer on top of the default file logger. If you still need a log file after overriding the logger, add file output inside your custom logger configuration.
Prints live [HH:mm:ss] requests/ok/fail snapshots separately from the logger when realtime metrics are enabled. Those snapshot lines still appear even when the normal logger pipeline is customized.
JSON and CLI configuration accept the same public level names plus numeric 0 through 5 tokens. Invalid values are ignored so the current/default level stays active.
Because .NET uses Serilog LoggerConfiguration, the same callback can attach any Serilog sink package already referenced by the application, such as file, Seq, Elasticsearch, or database sinks.