diff --git a/load-generator/README.md b/load-generator/README.md index 9f234d8b9..43134fe62 100644 --- a/load-generator/README.md +++ b/load-generator/README.md @@ -1,21 +1,18 @@ ### AWS OTel Collector Load Test Generator ### Parameters for Metric Generation: -1. Metric Count\ - --metricCount, -m : (default=1) the number of metrics that should be generated for each metric type. This amount of metrics will be updated/exported per period. - -2. Datapoint Count\ - --datapointCount, -dp : (default=1) the number of data-points that should be created for each metric. Number of data-points updated/exported per period shall be equal to Metric Count * Datapoint Count. - -3. Observation Interval\ - --observationInterval, -o : (default=1000 ms) the interval (in ms) at which metric observations/values are updated. +1. Rate\ + --rate, -r (eg, 1,000, 10,000 metric counts per second) : (default=1) the number of metrics that should be generated for each metric type. This amount of metrics will be updated/exported per period. -4. Collector Receiver Endpoint\ +2. Collector Receiver Endpoint\ --url, -u -5. Data Format\ +3. Data Format\ --dataFormat -d (eg, otlp, statsd) +4. Metric Type + --metricType, -mt : (default=counter) Specify the type of metric - counter or gauge. + ### Parameters for Trace Generation: 1. Rate\ @@ -28,13 +25,8 @@ --dataFormat -d (eg, otlp, prometheus, xray) ### OTLP Metrics Load Test Sample Command, - -The following is a list of additional optional command line arguments applicable only for configuration of OTLP metrics: -* `--flushInterval, -f` : (default=1000 ms) the metric collection export interval (in ms). -* `--metricType, -mt`: (default=counter) Specify the type of metric - counter or gauge. - ``` -./gradlew :load-generator:run --args="metric --metricCount=100 --datapointCount=10 --observationInterval=1000 --flushInterval=1000 --metricType=counter -u=localhost:4317 -d=otlp" +./gradlew :load-generator:run --args="metric -r=100 -u=localhost:4317 --metricType=counter -d=otlp" ``` ### OTLP Trace Load Test Sample Command, @@ -49,7 +41,7 @@ The following is a list of additional optional command line arguments applicable ### StatsD Metrics Load Test Sample Command, ``` -./gradlew :load-generator:run --args="metric --metricCount=100 --datapointCount=10 --observationInterval=1000 -u=localhost:8125 -d=statsd" +./gradlew :load-generator:run --args="metric -r=100 -u=localhost:8125 --metricType=counter -d=statsd" ``` ### Zipkin Trace Load Test Sample Command, diff --git a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/CommonOption.java b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/CommonOption.java index 992802db6..1be4385ec 100644 --- a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/CommonOption.java +++ b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/CommonOption.java @@ -22,6 +22,10 @@ @Command(footer = "Common parameter options for both Metric and Trace") public class CommonOption { + @Option(names = {"-r", "--rate"}, + description = "the number of data points will be sent per second", + defaultValue = "10") + private int rate; @Option(names = {"-u", "--url"}, description = "adot-collector receiver endpoint", defaultValue = "localhost:4317") @@ -35,6 +39,7 @@ public class CommonOption { public Parameter buildParameter() { return Parameter.builder() + .rate(rate) .dataFormat(dataFormat) .endpoint(endpoint) .build(); diff --git a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/MetricCommand.java b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/MetricCommand.java index 0f96a4913..25f230a11 100644 --- a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/MetricCommand.java +++ b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/MetricCommand.java @@ -19,7 +19,6 @@ import com.amazon.opentelemetry.load.generator.emitter.EmitterFactory; import com.amazon.opentelemetry.load.generator.model.DataType; import com.amazon.opentelemetry.load.generator.model.Parameter; -import java.util.concurrent.TimeUnit; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; import picocli.CommandLine.Command; @@ -36,26 +35,6 @@ public class MetricCommand implements Runnable { @Mixin CommonOption commonOption = new CommonOption(); - @Option(names = {"-f", "--flushInterval"}, - description = "the metric collection export interval (in ms)", - defaultValue = "1000") - private long flushIntervalMillis; - - @Option(names = {"-m", "--metricCount"}, - description = "the number of metrics that should be created for each metric type", - defaultValue = "1") - private int metricCount; - - @Option(names = {"-dp", "--datapointCount"}, - description = "the number of datapoints that should be created for each metric", - defaultValue = "1") - private int datapointCount; - - @Option(names = {"-o", "--observationInterval"}, - description = "the interval (in ms) at which metric observations/values are updated", - defaultValue = "1000") - private long observationIntervalMillis; - @Option(names = {"-mt", "--metricType"}, description = "Specify the type of metric - counter or gauge", defaultValue = "counter") @@ -65,13 +44,8 @@ public class MetricCommand implements Runnable { @Override public void run() { Parameter param = commonOption.buildParameter(); - param.setFlushInterval(flushIntervalMillis); - param.setMetricCount(metricCount); - param.setDatapointCount(datapointCount); - param.setObservationInterval(observationIntervalMillis); param.setMetricType(metricType); - log.info("param: {} " + param); Emitter emitter = EmitterFactory.getEmitter(param, DataType.Metric); diff --git a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/TraceCommand.java b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/TraceCommand.java index dd54530cc..3580ec2eb 100644 --- a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/TraceCommand.java +++ b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/command/TraceCommand.java @@ -21,7 +21,6 @@ import com.amazon.opentelemetry.load.generator.model.Parameter; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; -import picocli.CommandLine; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; @@ -35,17 +34,10 @@ public class TraceCommand implements Runnable { @Mixin CommonOption commonOption = new CommonOption(); - @CommandLine.Option(names = {"-r", "--rate"}, - description = "the number of data points will be sent per second", - defaultValue = "10") - private int rate; - @SneakyThrows @Override public void run() { Parameter param = commonOption.buildParameter(); - param.setRate(rate); - log.info("param: {} " + param); Emitter emitter = EmitterFactory.getEmitter(param, DataType.Trace); diff --git a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/Emitter.java b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/Emitter.java index bb348b9cd..2b4c91ae6 100644 --- a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/Emitter.java +++ b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/Emitter.java @@ -21,6 +21,9 @@ public interface Emitter { int NUM_THREADS = 5; + + final long FLUSH_INTERVAL = 1000; + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(NUM_THREADS); void emitDataLoad() throws Exception; diff --git a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/MetricEmitter.java b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/MetricEmitter.java index 88ce1164b..92ae0d7a2 100644 --- a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/MetricEmitter.java +++ b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/MetricEmitter.java @@ -22,7 +22,6 @@ @Log4j2 public abstract class MetricEmitter implements Emitter { - protected static final String DIMENSION_API_NAME = "apiName"; protected static final String DIMENSION_STATUS_CODE = "statusCode"; protected static String API_COUNTER_METRIC = "apiBytesSent"; @@ -32,8 +31,8 @@ public abstract class MetricEmitter implements Emitter { @Override public void start(Runnable emitter) { - log.info("Generating metrics at a rate of(ms) : {}" , this.param.getObservationInterval()); - scheduler.scheduleAtFixedRate(emitter, 0, this.param.getObservationInterval(), TimeUnit.MILLISECONDS); + log.info("Generating metrics at a rate of(ms) : {}" , FLUSH_INTERVAL); + scheduler.scheduleAtFixedRate(emitter, 0, FLUSH_INTERVAL, TimeUnit.MILLISECONDS); }; } diff --git a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/OtlpMetricEmitter.java b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/OtlpMetricEmitter.java index 09fcbc43d..5c974843a 100644 --- a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/OtlpMetricEmitter.java +++ b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/OtlpMetricEmitter.java @@ -29,127 +29,115 @@ import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import lombok.extern.log4j.Log4j2; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; import java.time.Duration; +import java.util.concurrent.ThreadLocalRandom; @Log4j2 public class OtlpMetricEmitter extends MetricEmitter { - private final static String API_NAME = "loadTest"; - private final static String DataPointID = "datapoint_id"; - private LongCounter[] counters; - private long[] gaugeValues; - - - public OtlpMetricEmitter(Parameter param) { - super(); - this.param = param; - } - - @Override - public void emitDataLoad() throws Exception { - this.setupProvider(); - this.start(() -> nextDataPoint()); - } - - @Override - public void nextDataPoint() { - log.debug("Updating metrics for : {}" , param.getMetricType()); - - switch (param.getMetricType()) { - case "counter": - for(LongCounter counter: counters) { - for(int id=0 ; id < param.getDatapointCount(); id++) { - Attributes datapointAttributes = getDataPointAttributes(id); - counter.add(ThreadLocalRandom.current().nextInt(1,10), datapointAttributes); - } + private final static String API_NAME = "loadTest"; + private LongCounter[] counters; + private long[] gaugeValues; + + + public OtlpMetricEmitter(Parameter param) { + super(); + this.param = param; + } + + @Override + public void emitDataLoad() throws Exception { + this.setupProvider(); + this.start(() -> nextDataPoint()); + } + + @Override + public void nextDataPoint() { + log.debug("Updating metrics for : {}", param.getMetricType()); + + switch (param.getMetricType()) { + case "counter": + for (LongCounter counter : counters) { + Attributes atr = Attributes.of(AttributeKey.stringKey(DIMENSION_API_NAME), API_NAME, + AttributeKey.stringKey(DIMENSION_STATUS_CODE), "200"); + counter.add(ThreadLocalRandom.current().nextInt(1, 10), atr); + } + break; + case "gauge": + for (int i = 0; i < param.getRate(); i++) { + this.gaugeValues[i] = ThreadLocalRandom.current().nextLong(-100, 100); + } + break; } - break; - case "gauge": - for(int i=0 ; i < param.getMetricCount(); i++) { - for (int id = 0; id < param.getDatapointCount(); id++) { - this.gaugeValues[id] = ThreadLocalRandom.current().nextLong(-100, 100); - } + } + + @Override + public void setupProvider() { + log.info("Setting up metric provider to generate metric load..."); + + Resource resource = Resource.getDefault().merge( + Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "load-generator"))); + + OpenTelemetrySdk.builder().setMeterProvider( + SdkMeterProvider.builder() + .registerMetricReader( + PeriodicMetricReader.builder(OtlpGrpcMetricExporter.builder().setEndpoint("http://" + param.getEndpoint()).build()) + .setInterval(Duration.ofMillis(FLUSH_INTERVAL)).build()) + .setResource(resource) + .build()) + .buildAndRegisterGlobal(); + + Meter meter = + GlobalOpenTelemetry.meterBuilder("adot-load-generator-metric") + .setInstrumentationVersion("0.1.0") + .build(); + log.info("Finished Setting up metric provider."); + switch (param.getMetricType()) { + case "counter": + createCounters(meter); + break; + case "gauge": + createGauges(meter); + break; } - break; } - } - - private Attributes getDataPointAttributes(int id) { - Attributes atr = Attributes.of(AttributeKey.stringKey(DataPointID),String.valueOf(id), AttributeKey.stringKey(DIMENSION_API_NAME), API_NAME, - AttributeKey.stringKey(DIMENSION_STATUS_CODE), "200"); - return atr; - } - - @Override - public void setupProvider() { - log.info("Setting up metric provider to generate metric load..."); - - Resource resource = Resource.getDefault().merge( - Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "load-generator"))); - - OpenTelemetrySdk.builder().setMeterProvider( - SdkMeterProvider.builder() - .registerMetricReader( - PeriodicMetricReader.builder(OtlpGrpcMetricExporter.builder().setEndpoint("http://" + param.getEndpoint()).build()) - .setInterval(Duration.ofMillis(param.getFlushInterval())).build()) - .setResource(resource) - - .build()) - .buildAndRegisterGlobal(); - - Meter meter = - GlobalOpenTelemetry.meterBuilder("adot-load-generator-metric") - .setInstrumentationVersion("0.1.0") + + private void createCounters(Meter meter) { + if (meter == null) { + log.error("Metric provider was not found!"); + return; + } + log.info("Registering counter metrics..."); + counters = new LongCounter[param.getRate()]; + for (int i = 0; i < param.getRate(); i++) { + counters[i] = meter.counterBuilder(API_COUNTER_METRIC + i) + .setDescription("API request load sent in bytes") + .setUnit("one") .build(); - log.info("Finished Setting up metric provider."); - switch (param.getMetricType()) { - case "counter": - createCounters(meter); - break; - case "gauge": - createGauges(meter); - break; - } - } - - private void createCounters(Meter meter) { - if(meter!= null) { - log.info("Registering counter metrics..."); - counters = new LongCounter[param.getDatapointCount()]; - for(int id=0 ; id < param.getMetricCount(); id++) { - counters[id] = meter.counterBuilder(API_COUNTER_METRIC + id) - .setDescription("API request load sent in bytes") - .setUnit("one") - .build(); - } - } else { - log.error("Metric provider was not found!"); + } } - } - - private void createGauges(Meter meter) { - if(meter != null) { - log.info("Registering gauge metrics..."); - gaugeValues = new long[param.getDatapointCount()]; - for (int i = 0; i < param.getMetricCount(); i++) { - meter.gaugeBuilder(API_LATENCY_METRIC + i) - .setDescription("API latency time") - .setUnit("ms") - .ofLongs().buildWithCallback(measurement -> - { - for (int id = 0; id < param.getDatapointCount(); id++) { - Attributes datapointAttributes = getDataPointAttributes(id); - measurement.record(gaugeValues[id], datapointAttributes); - } - } - ); - } - } else { - log.error("Metric provider was not found!"); + + private void createGauges(Meter meter) { + if (meter == null) { + log.error("Metric provider was not found!"); + return; + } + log.info("Registering gauge metrics..."); + gaugeValues = new long[param.getRate()]; + int id = 0; + for (long gaugeValue : gaugeValues) { + meter.gaugeBuilder(API_LATENCY_METRIC + id++) + .setDescription("API latency time") + .setUnit("ms") + .ofLongs().buildWithCallback(measurement -> + { + Attributes atr = Attributes.of(AttributeKey.stringKey(DIMENSION_API_NAME), API_NAME, + AttributeKey.stringKey(DIMENSION_STATUS_CODE), "200"); + measurement.record(gaugeValue, atr); + } + ); + } + } - } } \ No newline at end of file diff --git a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/OtlpTraceEmitter.java b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/OtlpTraceEmitter.java index 32c7291c0..ee7d4dd6f 100644 --- a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/OtlpTraceEmitter.java +++ b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/OtlpTraceEmitter.java @@ -27,8 +27,6 @@ import io.opentelemetry.sdk.extension.aws.trace.AwsXrayIdGenerator; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; -import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; -import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import java.time.Duration; @@ -59,7 +57,9 @@ public void setupProvider() throws Exception { .setTracerProvider( SdkTracerProvider.builder() .addSpanProcessor( - BatchSpanProcessor.builder(OtlpGrpcSpanExporter.getDefault()).build()) + BatchSpanProcessor.builder(OtlpGrpcSpanExporter.getDefault()) + .setMaxQueueSize(5000) + .setScheduleDelay(Duration.ofMillis(FLUSH_INTERVAL)).build()) .setIdGenerator(AwsXrayIdGenerator.getInstance()) .setResource(resource) .build()) @@ -74,7 +74,6 @@ public void setupProvider() throws Exception { @Override public void nextDataPoint() { Span exampleSpan = tracer.spanBuilder("Example Span").setSpanKind(SpanKind.SERVER).startSpan(); - exampleSpan.setAttribute("good", "true"); exampleSpan.setAttribute("exampleNumber", UUID.randomUUID().toString()); exampleSpan.end(); diff --git a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/StatsdMetricEmitter.java b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/StatsdMetricEmitter.java index f8f8b635c..f1f954459 100644 --- a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/StatsdMetricEmitter.java +++ b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/StatsdMetricEmitter.java @@ -18,7 +18,6 @@ import com.amazon.opentelemetry.load.generator.model.Parameter; import lombok.extern.log4j.Log4j2; - import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; @@ -26,7 +25,7 @@ import java.util.concurrent.ThreadLocalRandom; @Log4j2 -public class StatsdMetricEmitter extends MetricEmitter{ +public class StatsdMetricEmitter extends MetricEmitter { InetAddress ipAddress; int portAddress; @@ -46,8 +45,8 @@ public void emitDataLoad() throws Exception { @Override public void setupProvider() throws Exception { log.info("Setting up Statsd metric emitter to generate load..."); - ipAddress = InetAddress.getByName(param.getEndpoint().split(":",2)[0]); - portAddress = Integer.parseInt(param.getEndpoint().split(":",2)[1]); + ipAddress = InetAddress.getByName(param.getEndpoint().split(":", 2)[0]); + portAddress = Integer.parseInt(param.getEndpoint().split(":", 2)[1]); socket = new DatagramSocket(); log.info("Finished Setting up Statsd metric emitter."); } @@ -68,17 +67,15 @@ public void sendStatsd() throws IOException { payload = new StringBuilder(); String attributes = "#mykey:myvalue,datapoint_id:"; log.debug("Updating metrics..."); - for (int i = 0; i < this.param.getMetricCount(); i++) { - for (int id = 0; id < this.param.getDatapointCount(); id++) { - payload.append("statsdTestMetric").append(i).append(":") - .append(ThreadLocalRandom.current().nextInt(-100,100)) - .append("|g|").append(attributes).append(id).append("\n"); - } + for (int i = 0; i < this.param.getRate(); i++) { + payload.append("statsdTestMetric").append(i).append(":") + .append(ThreadLocalRandom.current().nextInt(-100, 100)) + .append("|g|").append(attributes).append(i).append("\n"); } buf = payload.toString().getBytes(); log.debug("Payload size: %d", buf.length); if (buf.length >= 64000) { - throw new RuntimeException ("Reduce the number of metrics/data-points. UDP packets have size limitation of 64k"); + throw new RuntimeException("Reduce the number of metrics/data-points. UDP packets have size limitation of 64k"); } DatagramPacket packet = new DatagramPacket(buf, buf.length, ipAddress, portAddress); socket.send(packet); diff --git a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/TraceEmitter.java b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/TraceEmitter.java index 60e454825..13e57074e 100644 --- a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/TraceEmitter.java +++ b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/emitter/TraceEmitter.java @@ -24,7 +24,7 @@ public abstract class TraceEmitter implements Emitter { @Override public void start(Runnable emitter) { - scheduler.scheduleAtFixedRate(emitter, TimeUnit.SECONDS.toMillis(5000), + scheduler.scheduleAtFixedRate(emitter, 0, TimeUnit.SECONDS.toNanos(1) / this.param.getRate(), TimeUnit.NANOSECONDS); }; } diff --git a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/model/Parameter.java b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/model/Parameter.java index ebcff6953..5757ddd94 100644 --- a/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/model/Parameter.java +++ b/load-generator/src/main/java/com/amazon/opentelemetry/load/generator/model/Parameter.java @@ -27,12 +27,8 @@ public class Parameter { private int rate; - private long flushInterval; private String dataFormat; private String endpoint; - private int metricCount; - private int datapointCount; - private long observationInterval; private String metricType; } diff --git a/terraform/ec2_setup/variables.tf b/terraform/ec2_setup/variables.tf index bdba81f02..b07d1a356 100644 --- a/terraform/ec2_setup/variables.tf +++ b/terraform/ec2_setup/variables.tf @@ -21,7 +21,7 @@ variable "sidecar_instance_type" { } variable "soaking_sample_app_image" { - default = "public.ecr.aws/aws-otel-test/aws-otel-load-generator:v0.11.0" + default = "public.ecr.aws/aws-otel-test/aws-otel-load-generator:latest" } variable "soaking_sample_app" {