Skip to content

Instantly share code, notes, and snippets.

@andsel
Created December 23, 2025 09:03
Show Gist options
  • Select an option

  • Save andsel/d3b372b90bd66e0db98a6acc1ac32c80 to your computer and use it in GitHub Desktop.

Select an option

Save andsel/d3b372b90bd66e0db98a6acc1ac32c80 to your computer and use it in GitHub Desktop.
Simple plain text traffic loader for Logstash
///usr/bin/env jbang "$0" "$@" ; exit $?
//DESCRIPTION A traffic simulation application
//DEPS info.picocli:picocli:4.6.3
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.Socket;
import java.time.Duration;
import java.time.LocalDateTime;
import java.net.ConnectException;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
@Command(name = "traffic-simulator", mixinStandardHelpOptions = true, version = "traffic-simulator 0.1",
description = "A traffic simulation application")
class TrafficSimulator implements Callable<Integer> {
@Option(names = {"-h", "--host"}, description = "The remote host to connect to", defaultValue = "localhost")
private String host = "localhost";
@Option(names = {"-p", "--port"}, description = "The port number to connect to", defaultValue = "3333")
private int port = 3333;
@Option(names = {"--mean-size"}, description = "Mean message size in characters", defaultValue = "50")
private int meanSize = 50;
@Option(names = {"--delta"}, description = "Message size variation (+/- delta)", defaultValue = "20")
private int delta = 20;
private final Random random = new Random();
/**
* Generates a message with variable size based on mean +/- delta
* @param meanSize the target mean size of the message
* @param delta the variation range (message size will be between meanSize-delta and meanSize+delta)
* @return a generated message string
*/
private String generateMessage(int meanSize, int delta) {
int actualSize = Math.max(1, meanSize - delta + random.nextInt(2 * delta + 1));
StringBuilder message = new StringBuilder(actualSize);
// Fill with readable characters
// String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ";
for (int i = 0; i < actualSize; i++) {
// message.append(chars.charAt(random.nextInt(chars.length())));
message.append("a");
}
return message.toString();
}
private void sendBatch(PrintWriter printWriter, int meanSize, int delta, int batchSize) {
for (int i = 0; i < batchSize; i++) {
String message = generateMessage(meanSize, delta);
printWriter.println(message);
}
printWriter.flush();
}
public static void main(String... args) {
int exitCode = new CommandLine(new TrafficSimulator()).execute(args);
System.exit(exitCode);
}
record StressPhase(String name, Duration duration, int meanSize, int delta, int batchSize) {}
final int batchSize = 100;
final List<StressPhase> experimentPhases = Arrays.asList(
new StressPhase("1 minute", Duration.ofMinutes(1), 4 * 1024, 200, batchSize),
new StressPhase("5 minutes", Duration.ofMinutes(4), 2 * 1024, 100, batchSize),
new StressPhase("15 minutes", Duration.ofMinutes(10), 1024, 50, batchSize)
);
@Override
public Integer call() throws Exception {
System.out.println("Starting traffic simulation to " + host + ":" + port + "...");
// Create a socket and connect to the host and port
Socket socket = null;
try {
socket = new Socket(host, port);
} catch (ConnectException e) {
System.out.println("Failed to connect to " + host + ":" + port + ": " + e.getMessage());
return 1;
}
// Get the output stream
// Wrap with PrintWriter for newline support
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
for (StressPhase phase : experimentPhases) {
System.out.println("Starting phase: " + phase.name() + ", " + LocalDateTime.now());
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < phase.duration.toMillis()) {
// TODO now the number of messages per batch is fixed, make it little bit more dynamic to different numbers in percentiles.
// create a what should be a single batch on Logstash side
sendBatch(printWriter, phase.meanSize, phase.delta, phase.batchSize);
Thread.sleep(20); // sleep an interval shorter than the batch interval (pipeline.batch.delay default 50 ms)
}
}
printWriter.close();
// Close the socket
socket.close();
// TODO: Implement traffic simulation logic
System.out.println("Traffic simulation completed!");
return 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment