mirror of
https://github.com/elastic/logstash.git
synced 2025-06-28 09:46:03 -04:00
parent
10a72ce015
commit
d9436dd760
27 changed files with 1760 additions and 1 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -49,3 +49,4 @@ qa/integration/services/installed/
|
||||||
**/.classpath
|
**/.classpath
|
||||||
logstash-core/bin
|
logstash-core/bin
|
||||||
plugins_version_docs.json
|
plugins_version_docs.json
|
||||||
|
tools/benchmark-cli/out/
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
include ':logstash-core', 'logstash-core-benchmarks', 'ingest-converter'
|
include ':logstash-core', 'logstash-core-benchmarks', 'ingest-converter', 'benchmark-cli'
|
||||||
project(':logstash-core').projectDir = new File('./logstash-core')
|
project(':logstash-core').projectDir = new File('./logstash-core')
|
||||||
project(':logstash-core-benchmarks').projectDir = new File('./logstash-core/benchmarks')
|
project(':logstash-core-benchmarks').projectDir = new File('./logstash-core/benchmarks')
|
||||||
project(':ingest-converter').projectDir = new File('./tools/ingest-converter')
|
project(':ingest-converter').projectDir = new File('./tools/ingest-converter')
|
||||||
|
project(':benchmark-cli').projectDir = new File('./tools/benchmark-cli')
|
||||||
|
|
71
tools/benchmark-cli/README.md
Normal file
71
tools/benchmark-cli/README.md
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
### Benchmark CLI
|
||||||
|
|
||||||
|
#### Build
|
||||||
|
|
||||||
|
To build a self-contained archive of the benchmark tool simply run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gradle clean assemble
|
||||||
|
```
|
||||||
|
|
||||||
|
which will create the output jar under `build/libs/benchmark-cli.jar`.
|
||||||
|
|
||||||
|
#### Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ java -cp 'benchmark-cli.jar:*' org.logstash.benchmark.cli.Main --help
|
||||||
|
Option Description
|
||||||
|
------ -----------
|
||||||
|
--distribution-version <String> The version of a Logstash build to download
|
||||||
|
from elastic.co.
|
||||||
|
--git-hash <String> Either a git tree (tag/branch or commit hash),
|
||||||
|
optionally prefixed by a Github username,
|
||||||
|
if ran against forks.
|
||||||
|
E.g.
|
||||||
|
'ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7',
|
||||||
|
'user-
|
||||||
|
name#ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7'
|
||||||
|
or 'master'
|
||||||
|
--local-path <String> Path to the root of a local Logstash
|
||||||
|
distribution.
|
||||||
|
E.g. `/opt/logstash`
|
||||||
|
--testcase <String> Currently available test cases are 'baseline'
|
||||||
|
and 'apache'. (default: baseline)
|
||||||
|
--workdir <File> Working directory to store cached files in.
|
||||||
|
(default: ~/.logstash-benchmarks)
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ java -cp 'benchmark-cli.jar:*' org.logstash.benchmark.cli.Main --workdir=/tmp/benchmark2 --testcase=baseline --distribution-version=5.5.0
|
||||||
|
██╗ ██████╗ ██████╗ ███████╗████████╗ █████╗ ███████╗██╗ ██╗
|
||||||
|
██║ ██╔═══██╗██╔════╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██║
|
||||||
|
██║ ██║ ██║██║ ███╗███████╗ ██║ ███████║███████╗███████║
|
||||||
|
██║ ██║ ██║██║ ██║╚════██║ ██║ ██╔══██║╚════██║██╔══██║
|
||||||
|
███████╗╚██████╔╝╚██████╔╝███████║ ██║ ██║ ██║███████║██║ ██║
|
||||||
|
╚══════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
|
||||||
|
|
||||||
|
██████╗ ███████╗███╗ ██╗ ██████╗██╗ ██╗███╗ ███╗ █████╗ ██████╗ ██╗ ██╗
|
||||||
|
██╔══██╗██╔════╝████╗ ██║██╔════╝██║ ██║████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝
|
||||||
|
██████╔╝█████╗ ██╔██╗ ██║██║ ███████║██╔████╔██║███████║██████╔╝█████╔╝
|
||||||
|
██╔══██╗██╔══╝ ██║╚██╗██║██║ ██╔══██║██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗
|
||||||
|
██████╔╝███████╗██║ ╚████║╚██████╗██║ ██║██║ ╚═╝ ██║██║ ██║██║ ██║██║ ██╗
|
||||||
|
╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
|
||||||
|
|
||||||
|
------------------------------------------
|
||||||
|
Benchmarking Version: 5.5.0
|
||||||
|
Running Test Case: baseline
|
||||||
|
------------------------------------------
|
||||||
|
Start Time: Sat 7 22 21:28:45.4 2017 CEST
|
||||||
|
Statistical Summary:
|
||||||
|
|
||||||
|
Elapsed Time: 33s
|
||||||
|
Num Events: 977816
|
||||||
|
Throughput Min: 2000.00
|
||||||
|
Throughput Max: 44500.00
|
||||||
|
Throughput Mean: 37608.31
|
||||||
|
Throughput StdDev: 8985.03
|
||||||
|
Throughput Variance: 80730818.62
|
||||||
|
Mean CPU Usage: 19.27%
|
||||||
|
```
|
57
tools/benchmark-cli/build.gradle
Normal file
57
tools/benchmark-cli/build.gradle
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import org.yaml.snakeyaml.Yaml
|
||||||
|
|
||||||
|
// fetch version from Logstash's master versions.yml file
|
||||||
|
def versionMap = (Map) (new Yaml()).load(new File("$projectDir/../../versions.yml").text)
|
||||||
|
|
||||||
|
description = """Logstash End to End Benchmarking Utility"""
|
||||||
|
version = versionMap['logstash-core']
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'org.yaml:snakeyaml:1.17'
|
||||||
|
classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.4'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ext {
|
||||||
|
jmh = 1.18
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile 'net.sf.jopt-simple:jopt-simple:5.0.3'
|
||||||
|
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3'
|
||||||
|
compile group: 'org.apache.commons', name: 'commons-compress', version: '1.14'
|
||||||
|
compile group: 'commons-io', name: 'commons-io', version: '2.5'
|
||||||
|
compile 'com.fasterxml.jackson.core:jackson-core:2.7.4'
|
||||||
|
compile 'com.fasterxml.jackson.core:jackson-databind:2.7.4'
|
||||||
|
compile "org.openjdk.jmh:jmh-core:$jmh"
|
||||||
|
testCompile group: 'com.github.tomakehurst', name: 'wiremock-standalone', version: '2.6.0'
|
||||||
|
testCompile "junit:junit:4.12"
|
||||||
|
}
|
||||||
|
|
||||||
|
javadoc {
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
exclude '**/org/logstash/benchmark/cli/MainTest*'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.github.johnrengelman.shadow'
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
baseName = 'benchmark-cli'
|
||||||
|
classifier = null
|
||||||
|
version = null
|
||||||
|
}
|
||||||
|
|
||||||
|
assemble.dependsOn shadowJar
|
29
tools/benchmark-cli/out/production/resources/apache.cfg
Normal file
29
tools/benchmark-cli/out/production/resources/apache.cfg
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
input {
|
||||||
|
stdin { }
|
||||||
|
}
|
||||||
|
|
||||||
|
filter {
|
||||||
|
grok {
|
||||||
|
match => {
|
||||||
|
"message" => '%{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "%{WORD:verb} %{DATA:request} HTTP/%{NUMBER:httpversion}" %{NUMBER:response:int} (?:-|%{NUMBER:bytes:int}) %{QS:referrer} %{QS:agent}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
date {
|
||||||
|
match => [ "timestamp", "dd/MMM/YYYY:HH:mm:ss Z" ]
|
||||||
|
locale => en
|
||||||
|
}
|
||||||
|
|
||||||
|
geoip {
|
||||||
|
source => "clientip"
|
||||||
|
}
|
||||||
|
|
||||||
|
useragent {
|
||||||
|
source => "agent"
|
||||||
|
target => "useragent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
stdout { codec => dots }
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.logstash.benchmark.apache.dataset.url=https://s3.amazonaws.com/data.elasticsearch.org/apache_logs/apache_access_logs.tar.gz
|
|
@ -0,0 +1,171 @@
|
||||||
|
{
|
||||||
|
"host": "localhost",
|
||||||
|
"version": "6.0.0-alpha3",
|
||||||
|
"http_address": "127.0.0.1:9600",
|
||||||
|
"id": "8bbabc13-ea58-4dcd-b94e-90ae5f692c17",
|
||||||
|
"name": "localhost",
|
||||||
|
"jvm": {
|
||||||
|
"threads": {
|
||||||
|
"count": 28,
|
||||||
|
"peak_count": 28
|
||||||
|
},
|
||||||
|
"mem": {
|
||||||
|
"heap_used_percent": 16,
|
||||||
|
"heap_committed_in_bytes": 259522560,
|
||||||
|
"heap_max_in_bytes": 1037959168,
|
||||||
|
"heap_used_in_bytes": 168360000,
|
||||||
|
"non_heap_used_in_bytes": 113241032,
|
||||||
|
"non_heap_committed_in_bytes": 124989440,
|
||||||
|
"pools": {
|
||||||
|
"survivor": {
|
||||||
|
"peak_used_in_bytes": 8912896,
|
||||||
|
"used_in_bytes": 6872400,
|
||||||
|
"peak_max_in_bytes": 35782656,
|
||||||
|
"max_in_bytes": 35782656,
|
||||||
|
"committed_in_bytes": 8912896
|
||||||
|
},
|
||||||
|
"old": {
|
||||||
|
"peak_used_in_bytes": 141395984,
|
||||||
|
"used_in_bytes": 119128832,
|
||||||
|
"peak_max_in_bytes": 715849728,
|
||||||
|
"max_in_bytes": 715849728,
|
||||||
|
"committed_in_bytes": 178978816
|
||||||
|
},
|
||||||
|
"young": {
|
||||||
|
"peak_used_in_bytes": 71630848,
|
||||||
|
"used_in_bytes": 42358768,
|
||||||
|
"peak_max_in_bytes": 286326784,
|
||||||
|
"max_in_bytes": 286326784,
|
||||||
|
"committed_in_bytes": 71630848
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gc": {
|
||||||
|
"collectors": {
|
||||||
|
"old": {
|
||||||
|
"collection_time_in_millis": 89,
|
||||||
|
"collection_count": 3
|
||||||
|
},
|
||||||
|
"young": {
|
||||||
|
"collection_time_in_millis": 516,
|
||||||
|
"collection_count": 36
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uptime_in_millis": 15055
|
||||||
|
},
|
||||||
|
"process": {
|
||||||
|
"open_file_descriptors": 63,
|
||||||
|
"peak_open_file_descriptors": 63,
|
||||||
|
"max_file_descriptors": 10240,
|
||||||
|
"mem": {
|
||||||
|
"total_virtual_in_bytes": 5335916544
|
||||||
|
},
|
||||||
|
"cpu": {
|
||||||
|
"total_in_millis": 67919,
|
||||||
|
"percent": 63,
|
||||||
|
"load_average": {
|
||||||
|
"1m": 2.6826171875
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"in": 23101,
|
||||||
|
"filtered": 21052,
|
||||||
|
"out": 21052,
|
||||||
|
"duration_in_millis": 8939,
|
||||||
|
"queue_push_duration_in_millis": 3978
|
||||||
|
},
|
||||||
|
"pipelines": {
|
||||||
|
"main": {
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 9250,
|
||||||
|
"in": 24125,
|
||||||
|
"filtered": 22076,
|
||||||
|
"out": 22076,
|
||||||
|
"queue_push_duration_in_millis": 4236
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-1",
|
||||||
|
"events": {
|
||||||
|
"out": 24125,
|
||||||
|
"queue_push_duration_in_millis": 4236
|
||||||
|
},
|
||||||
|
"name": "stdin"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-4",
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 374,
|
||||||
|
"in": 23045,
|
||||||
|
"out": 23044
|
||||||
|
},
|
||||||
|
"name": "geoip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-3",
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 24,
|
||||||
|
"in": 23045,
|
||||||
|
"out": 23045
|
||||||
|
},
|
||||||
|
"matches": 23045,
|
||||||
|
"name": "date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-5",
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 1373,
|
||||||
|
"in": 23045,
|
||||||
|
"out": 23045
|
||||||
|
},
|
||||||
|
"name": "useragent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-2",
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 295,
|
||||||
|
"in": 23047,
|
||||||
|
"out": 23045
|
||||||
|
},
|
||||||
|
"matches": 23045,
|
||||||
|
"patterns_per_field": {
|
||||||
|
"message": 1
|
||||||
|
},
|
||||||
|
"name": "grok"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-6",
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 89,
|
||||||
|
"in": 22076,
|
||||||
|
"out": 22076
|
||||||
|
},
|
||||||
|
"name": "stdout"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"reloads": {
|
||||||
|
"last_error": null,
|
||||||
|
"successes": 0,
|
||||||
|
"last_success_timestamp": null,
|
||||||
|
"last_failure_timestamp": null,
|
||||||
|
"failures": 0
|
||||||
|
},
|
||||||
|
"queue": {
|
||||||
|
"type": "memory"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"reloads": {
|
||||||
|
"successes": 0,
|
||||||
|
"failures": 0
|
||||||
|
},
|
||||||
|
"os": {}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package org.logstash.benchmark.cli;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import org.logstash.benchmark.cli.util.LsBenchDownloader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JRuby Installation.
|
||||||
|
*/
|
||||||
|
public final class JRubyInstallation {
|
||||||
|
|
||||||
|
private static final String JRUBY_DEFAULT_VERSION = "9.1.10.0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path of the `gem` executable.
|
||||||
|
*/
|
||||||
|
private final Path gem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path of the `rake` executable.
|
||||||
|
*/
|
||||||
|
private final Path rake;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path of the `ruby` executable.
|
||||||
|
*/
|
||||||
|
private final Path jruby;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up a JRuby used to bootstrap a Logstash Installation.
|
||||||
|
* @param pwd Cache Directory to work in
|
||||||
|
* @return instance
|
||||||
|
* @throws IOException On I/O Error during JRuby Download or Installation
|
||||||
|
* @throws NoSuchAlgorithmException On SSL Issue during JRuby Download
|
||||||
|
*/
|
||||||
|
public static JRubyInstallation bootstrapJruby(final Path pwd)
|
||||||
|
throws IOException, NoSuchAlgorithmException {
|
||||||
|
LsBenchDownloader.downloadDecompress(
|
||||||
|
pwd.resolve("jruby").toFile(),
|
||||||
|
String.format(
|
||||||
|
"http://jruby.org.s3.amazonaws.com/downloads/%s/jruby-bin-%s.tar.gz",
|
||||||
|
JRUBY_DEFAULT_VERSION, JRUBY_DEFAULT_VERSION
|
||||||
|
),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return new JRubyInstallation(
|
||||||
|
pwd.resolve("jruby").resolve(String.format("jruby-%s", JRUBY_DEFAULT_VERSION))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JRubyInstallation(final Path root) {
|
||||||
|
final Path bin = root.resolve("bin");
|
||||||
|
this.gem = bin.resolve("gem");
|
||||||
|
this.jruby = bin.resolve("jruby");
|
||||||
|
this.rake = bin.resolve("rake");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String gem() {
|
||||||
|
return gem.toAbsolutePath().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String rake() {
|
||||||
|
return rake.toAbsolutePath().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String jruby() {
|
||||||
|
return jruby.toAbsolutePath().toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
package org.logstash.benchmark.cli;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.logstash.benchmark.cli.util.LsBenchDownloader;
|
||||||
|
import org.logstash.benchmark.cli.util.LsBenchFileUtil;
|
||||||
|
|
||||||
|
public interface LogstashInstallation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the Logstash Installation with the given configuration.
|
||||||
|
* @param configuration Configuration as String
|
||||||
|
* @throws IOException On I/O Exception
|
||||||
|
* @throws InterruptedException Iff Interrupted
|
||||||
|
*/
|
||||||
|
void execute(String configuration) throws IOException, InterruptedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the Logstash Installation with the given configuration and given File piped to
|
||||||
|
* standard input.
|
||||||
|
* @param configuration Configuration as String
|
||||||
|
* @param data Data file piped to standard input
|
||||||
|
* @throws IOException On I/O Exception
|
||||||
|
* @throws InterruptedException Iff Interrupted
|
||||||
|
*/
|
||||||
|
void execute(String configuration, File data) throws IOException, InterruptedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the url under which the metrics from uri `_node/stats/?pretty` can be found.
|
||||||
|
* @return Metrics URL
|
||||||
|
*/
|
||||||
|
String metrics();
|
||||||
|
|
||||||
|
final class FromRelease implements LogstashInstallation {
|
||||||
|
|
||||||
|
private final LogstashInstallation base;
|
||||||
|
|
||||||
|
public FromRelease(final File pwd, final String version) {
|
||||||
|
try {
|
||||||
|
LogstashInstallation.FromRelease.download(pwd, version);
|
||||||
|
this.base = LogstashInstallation.FromRelease.setup(
|
||||||
|
pwd.toPath().resolve(String.format("logstash-%s", version))
|
||||||
|
);
|
||||||
|
} catch (IOException | NoSuchAlgorithmException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final String configuration) throws IOException, InterruptedException {
|
||||||
|
base.execute(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final String configuration, final File data)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
base.execute(configuration, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String metrics() {
|
||||||
|
return base.metrics();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void download(final File pwd, final String version)
|
||||||
|
throws IOException, NoSuchAlgorithmException {
|
||||||
|
LsBenchDownloader.downloadDecompress(
|
||||||
|
pwd,
|
||||||
|
String.format(
|
||||||
|
"https://artifacts.elastic.co/downloads/logstash/logstash-%s.zip",
|
||||||
|
version
|
||||||
|
), true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LogstashInstallation setup(final Path location) {
|
||||||
|
return new LogstashInstallation.FromLocalPath(location.toAbsolutePath().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class FromLocalPath implements LogstashInstallation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metrics URL.
|
||||||
|
*/
|
||||||
|
private static final String METRICS_URL = "http://127.0.0.1:9600/_node/stats/?pretty";
|
||||||
|
|
||||||
|
private final Path location;
|
||||||
|
|
||||||
|
private final ProcessBuilder pbuilder;
|
||||||
|
|
||||||
|
public FromLocalPath(final String path) {
|
||||||
|
this.location = Paths.get(path);
|
||||||
|
this.pbuilder = new ProcessBuilder().directory(location.toFile());
|
||||||
|
final Path jruby = location.resolve("vendor").resolve("jruby");
|
||||||
|
LsBenchFileUtil.ensureExecutable(jruby.resolve("bin").resolve("jruby").toFile());
|
||||||
|
final Map<String, String> env = pbuilder.environment();
|
||||||
|
env.put("JRUBY_HOME", jruby.toString());
|
||||||
|
env.put("JAVA_OPTS", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final String configuration) throws IOException, InterruptedException {
|
||||||
|
execute(configuration, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final String configuration, final File data)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
final Path cfg = location.resolve("config.temp");
|
||||||
|
Files.write(
|
||||||
|
cfg,
|
||||||
|
configuration.getBytes(StandardCharsets.UTF_8),
|
||||||
|
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING,
|
||||||
|
StandardOpenOption.CREATE
|
||||||
|
);
|
||||||
|
final Path lsbin = location.resolve("bin").resolve("logstash");
|
||||||
|
LsBenchFileUtil.ensureExecutable(lsbin.toFile());
|
||||||
|
final Process process = pbuilder.command(lsbin.toString(), "-w", "2", "-f", cfg.toString()).redirectOutput(
|
||||||
|
ProcessBuilder.Redirect.to(new File("/dev/null"))
|
||||||
|
).start();
|
||||||
|
if (data != null) {
|
||||||
|
try (final InputStream file = new FileInputStream(data);
|
||||||
|
final OutputStream out = process.getOutputStream()) {
|
||||||
|
IOUtils.copy(file, out, 16 * 4096);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (process.waitFor() != 0) {
|
||||||
|
throw new IllegalStateException("Logstash failed to start!");
|
||||||
|
}
|
||||||
|
LsBenchFileUtil.ensureDeleted(cfg.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String metrics() {
|
||||||
|
return METRICS_URL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class FromGithub implements LogstashInstallation {
|
||||||
|
|
||||||
|
private final Path location;
|
||||||
|
|
||||||
|
private final LogstashInstallation base;
|
||||||
|
|
||||||
|
public FromGithub(final File pwd, final String hash, final JRubyInstallation jruby) {
|
||||||
|
this(pwd, "elastic", hash, jruby);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FromGithub(final File pwd, final String user, final String hash,
|
||||||
|
final JRubyInstallation jruby) {
|
||||||
|
this.location = pwd.toPath().resolve(String.format("logstash-%s", hash));
|
||||||
|
try {
|
||||||
|
LogstashInstallation.FromGithub.download(pwd, user, hash);
|
||||||
|
this.base = LogstashInstallation.FromGithub.setup(jruby, this.location);
|
||||||
|
} catch (IOException | InterruptedException | NoSuchAlgorithmException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final String configuration) throws IOException, InterruptedException {
|
||||||
|
base.execute(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final String configuration, final File data)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
base.execute(configuration, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String metrics() {
|
||||||
|
return base.metrics();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void download(final File pwd, final String user, final String hash)
|
||||||
|
throws IOException, NoSuchAlgorithmException {
|
||||||
|
LsBenchDownloader.downloadDecompress(
|
||||||
|
pwd, String.format("https://github.com/%s/logstash/archive/%s.zip", user, hash),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LogstashInstallation setup(final JRubyInstallation ruby, final Path location)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
final ProcessBuilder pbuilder = new ProcessBuilder().directory(location.toFile());
|
||||||
|
final Map<String, String> env = pbuilder.environment();
|
||||||
|
env.put("JRUBY_HOME", Paths.get(ruby.gem()).getParent().getParent().toString());
|
||||||
|
env.put("JAVA_OPTS", "");
|
||||||
|
final File gem = new File(ruby.gem());
|
||||||
|
if (!gem.setExecutable(true) ||
|
||||||
|
pbuilder.command(gem.getAbsolutePath(), "install", "rake").inheritIO()
|
||||||
|
.start().waitFor() != 0) {
|
||||||
|
throw new IllegalStateException("Bootstrapping Rake Failed!");
|
||||||
|
}
|
||||||
|
for (final File exec : location.resolve("bin").toFile().listFiles()) {
|
||||||
|
LsBenchFileUtil.ensureExecutable(exec);
|
||||||
|
}
|
||||||
|
final String rakepath = ruby.rake();
|
||||||
|
final File rake = new File(rakepath);
|
||||||
|
if (!location.resolve("gradlew").toFile().setExecutable(true) ||
|
||||||
|
!new File(ruby.jruby()).setExecutable(true)
|
||||||
|
|| !rake.setExecutable(true)
|
||||||
|
|| pbuilder.command(rakepath, "bootstrap").inheritIO().start()
|
||||||
|
.waitFor() != 0) {
|
||||||
|
throw new IllegalStateException("Bootstrapping Logstash Failed!");
|
||||||
|
}
|
||||||
|
if (pbuilder.command(rakepath, "plugin:install-default").inheritIO().start()
|
||||||
|
.waitFor() != 0) {
|
||||||
|
throw new IllegalStateException("Bootstrapping Logstash Default Plugins Failed!");
|
||||||
|
}
|
||||||
|
return new LogstashInstallation.FromLocalPath(location.toAbsolutePath().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package org.logstash.benchmark.cli;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.logstash.benchmark.cli.ui.LsMetricStats;
|
||||||
|
import org.openjdk.jmh.util.ListStatistics;
|
||||||
|
|
||||||
|
public final class LsMetricsMonitor implements Callable<EnumMap<LsMetricStats, ListStatistics>> {
|
||||||
|
|
||||||
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
private final String metrics;
|
||||||
|
|
||||||
|
private volatile boolean running = true;
|
||||||
|
|
||||||
|
public LsMetricsMonitor(final String metrics) {
|
||||||
|
this.metrics = metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EnumMap<LsMetricStats, ListStatistics> call() {
|
||||||
|
final ListStatistics stats = new ListStatistics();
|
||||||
|
long count = 0L;
|
||||||
|
final ListStatistics counts = new ListStatistics();
|
||||||
|
final ListStatistics cpu = new ListStatistics();
|
||||||
|
long start = System.nanoTime();
|
||||||
|
while (running) {
|
||||||
|
try {
|
||||||
|
TimeUnit.SECONDS.sleep(1L);
|
||||||
|
final long[] newcounts = getCounts();
|
||||||
|
final long newcount = newcounts[0];
|
||||||
|
if (newcount < 0L) {
|
||||||
|
start = System.nanoTime();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final long newstrt = System.nanoTime();
|
||||||
|
stats.addValue(
|
||||||
|
(double) (newcount - count) /
|
||||||
|
(double) TimeUnit.SECONDS.convert(newstrt - start, TimeUnit.NANOSECONDS)
|
||||||
|
);
|
||||||
|
start = newstrt;
|
||||||
|
count = newcount;
|
||||||
|
counts.addValue((double) count);
|
||||||
|
cpu.addValue(newcounts[1]);
|
||||||
|
} catch (final InterruptedException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final EnumMap<LsMetricStats, ListStatistics> result = new EnumMap<>(LsMetricStats.class);
|
||||||
|
result.put(LsMetricStats.THROUGHPUT, stats);
|
||||||
|
result.put(LsMetricStats.COUNT, counts);
|
||||||
|
result.put(LsMetricStats.CPU_USAGE, cpu);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long[] getCounts() {
|
||||||
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
try (final CloseableHttpClient client = HttpClientBuilder.create().build()) {
|
||||||
|
baos.reset();
|
||||||
|
try (final CloseableHttpResponse response = client
|
||||||
|
.execute(new HttpGet(metrics))) {
|
||||||
|
response.getEntity().writeTo(baos);
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
return new long[]{-1L, -1L};
|
||||||
|
}
|
||||||
|
final Map<String, Object> data =
|
||||||
|
OBJECT_MAPPER.readValue(baos.toByteArray(), HashMap.class);
|
||||||
|
final long count;
|
||||||
|
if (data.containsKey("pipeline")) {
|
||||||
|
count = getFiltered((Map<String, Object>) data.get("pipeline"));
|
||||||
|
|
||||||
|
} else if (data.containsKey("events")) {
|
||||||
|
count = getFiltered(data);
|
||||||
|
} else {
|
||||||
|
count = -1L;
|
||||||
|
}
|
||||||
|
final long cpu;
|
||||||
|
if (count == -1L) {
|
||||||
|
cpu = -1L;
|
||||||
|
} else {
|
||||||
|
cpu = ((Number) ((Map<String, Object>) ((Map<String, Object>) data.get("process"))
|
||||||
|
.get("cpu")).get("percent")).longValue();
|
||||||
|
}
|
||||||
|
return new long[]{count, cpu};
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long getFiltered(final Map<String, Object> data) {
|
||||||
|
return ((Number) ((Map<String, Object>) (data.get("events")))
|
||||||
|
.get("filtered")).longValue();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
package org.logstash.benchmark.cli;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import joptsimple.OptionException;
|
||||||
|
import joptsimple.OptionParser;
|
||||||
|
import joptsimple.OptionSet;
|
||||||
|
import joptsimple.OptionSpec;
|
||||||
|
import joptsimple.OptionSpecBuilder;
|
||||||
|
import org.logstash.benchmark.cli.cases.ApacheLogsComplex;
|
||||||
|
import org.logstash.benchmark.cli.cases.Case;
|
||||||
|
import org.logstash.benchmark.cli.cases.GeneratorToStdout;
|
||||||
|
import org.logstash.benchmark.cli.ui.LsMetricStats;
|
||||||
|
import org.logstash.benchmark.cli.ui.LsVersionType;
|
||||||
|
import org.logstash.benchmark.cli.ui.UserInput;
|
||||||
|
import org.logstash.benchmark.cli.ui.UserOutput;
|
||||||
|
import org.logstash.benchmark.cli.util.LsBenchLsSetup;
|
||||||
|
import org.openjdk.jmh.util.ListStatistics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Benchmark CLI Main Entry Point.
|
||||||
|
*/
|
||||||
|
public final class Main {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
*/
|
||||||
|
private Main() {
|
||||||
|
// Utility Class.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CLI Entrypoint.
|
||||||
|
* @param args Cli Args
|
||||||
|
*/
|
||||||
|
public static void main(final String... args) throws Exception {
|
||||||
|
final OptionParser parser = new OptionParser();
|
||||||
|
final OptionSpecBuilder gitbuilder =
|
||||||
|
parser.accepts(UserInput.GIT_VERSION_PARAM, UserInput.GIT_VERSION_HELP);
|
||||||
|
final OptionSpecBuilder localbuilder =
|
||||||
|
parser.accepts(UserInput.LOCAL_VERSION_PARAM, UserInput.LOCAL_VERSION_HELP);
|
||||||
|
final OptionSpecBuilder distributionbuilder =
|
||||||
|
parser.accepts(
|
||||||
|
UserInput.DISTRIBUTION_VERSION_PARAM, UserInput.DISTRIBUTION_VERSION_HELP
|
||||||
|
);
|
||||||
|
final OptionSpec<String> git = gitbuilder.requiredUnless(
|
||||||
|
UserInput.DISTRIBUTION_VERSION_PARAM, UserInput.LOCAL_VERSION_PARAM
|
||||||
|
).availableUnless(UserInput.DISTRIBUTION_VERSION_PARAM, UserInput.LOCAL_VERSION_PARAM)
|
||||||
|
.withRequiredArg().ofType(String.class).forHelp();
|
||||||
|
final OptionSpec<String> distribution = distributionbuilder
|
||||||
|
.requiredUnless(UserInput.LOCAL_VERSION_PARAM, UserInput.GIT_VERSION_PARAM)
|
||||||
|
.availableUnless(
|
||||||
|
UserInput.LOCAL_VERSION_PARAM, UserInput.GIT_VERSION_PARAM
|
||||||
|
).withRequiredArg().ofType(String.class).forHelp();
|
||||||
|
final OptionSpec<String> local = localbuilder.requiredUnless(
|
||||||
|
UserInput.DISTRIBUTION_VERSION_PARAM, UserInput.GIT_VERSION_PARAM)
|
||||||
|
.availableUnless(UserInput.DISTRIBUTION_VERSION_PARAM, UserInput.GIT_VERSION_PARAM)
|
||||||
|
.withRequiredArg().ofType(String.class).forHelp();
|
||||||
|
final OptionSpec<String> testcase = parser.accepts(
|
||||||
|
UserInput.TEST_CASE_PARAM, UserInput.TEST_CASE_HELP
|
||||||
|
).withRequiredArg().ofType(String.class).defaultsTo(GeneratorToStdout.IDENTIFIER).forHelp();
|
||||||
|
final OptionSpec<File> pwd = parser.accepts(
|
||||||
|
UserInput.WORKING_DIRECTORY_PARAM, UserInput.WORKING_DIRECTORY_HELP
|
||||||
|
).withRequiredArg().ofType(File.class).defaultsTo(UserInput.WORKING_DIRECTORY_DEFAULT)
|
||||||
|
.forHelp();
|
||||||
|
final OptionSet options;
|
||||||
|
try {
|
||||||
|
options = parser.parse(args);
|
||||||
|
} catch (final OptionException ex) {
|
||||||
|
parser.printHelpOn(System.out);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
final LsVersionType type;
|
||||||
|
final String version;
|
||||||
|
if (options.has(distribution)) {
|
||||||
|
type = LsVersionType.DISTRIBUTION;
|
||||||
|
version = options.valueOf(distribution);
|
||||||
|
} else if (options.has(git)) {
|
||||||
|
type = LsVersionType.GIT;
|
||||||
|
version = options.valueOf(git);
|
||||||
|
} else {
|
||||||
|
type = LsVersionType.LOCAL;
|
||||||
|
version = options.valueOf(local);
|
||||||
|
}
|
||||||
|
execute(
|
||||||
|
new UserOutput(System.out), loadSettings(), options.valueOf(testcase),
|
||||||
|
options.valueOf(pwd).toPath(), version, type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Programmatic Entrypoint.
|
||||||
|
* @param output Output Printer
|
||||||
|
* @param settings Properties
|
||||||
|
* @param test String identifier of the testcase to run
|
||||||
|
* @param cwd Working Directory to run in and write cache files to
|
||||||
|
* @param version Version of Logstash to benchmark
|
||||||
|
* @param type Type of Logstash version to benchmark
|
||||||
|
* @throws Exception On Failure
|
||||||
|
*/
|
||||||
|
public static void execute(final UserOutput output, final Properties settings,
|
||||||
|
final String test, final Path cwd, final String version, final LsVersionType type)
|
||||||
|
throws Exception {
|
||||||
|
output.printBanner();
|
||||||
|
output.printLine();
|
||||||
|
output.green(String.format("Benchmarking Version: %s", version));
|
||||||
|
output.green(String.format("Running Test Case: %s", test));
|
||||||
|
output.printLine();
|
||||||
|
Files.createDirectories(cwd);
|
||||||
|
final LogstashInstallation logstash;
|
||||||
|
if (type == LsVersionType.GIT) {
|
||||||
|
logstash = LsBenchLsSetup.logstashFromGit(
|
||||||
|
cwd.toAbsolutePath().toString(), version, JRubyInstallation.bootstrapJruby(cwd)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
logstash = LsBenchLsSetup.setupLS(cwd.toAbsolutePath().toString(), version, type);
|
||||||
|
}
|
||||||
|
final Case testcase;
|
||||||
|
if (GeneratorToStdout.IDENTIFIER.equalsIgnoreCase(test)) {
|
||||||
|
testcase = new GeneratorToStdout(logstash);
|
||||||
|
} else if (ApacheLogsComplex.IDENTIFIER.equalsIgnoreCase(test)) {
|
||||||
|
testcase = new ApacheLogsComplex(logstash, cwd, settings);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String.format("Unknown test case %s", test));
|
||||||
|
}
|
||||||
|
output.printStartTime();
|
||||||
|
final long start = System.currentTimeMillis();
|
||||||
|
final AbstractMap<LsMetricStats, ListStatistics> stats = testcase.run();
|
||||||
|
output.green("Statistical Summary:\n");
|
||||||
|
output.green(String.format(
|
||||||
|
"Elapsed Time: %ds",
|
||||||
|
TimeUnit.SECONDS.convert(
|
||||||
|
System.currentTimeMillis() - start, TimeUnit.MILLISECONDS
|
||||||
|
)
|
||||||
|
));
|
||||||
|
output.green(
|
||||||
|
String.format("Num Events: %d", (long) stats.get(LsMetricStats.COUNT).getMax())
|
||||||
|
);
|
||||||
|
final ListStatistics throughput = stats.get(LsMetricStats.THROUGHPUT);
|
||||||
|
output.green(String.format("Throughput Min: %.2f", throughput.getMin()));
|
||||||
|
output.green(String.format("Throughput Max: %.2f", throughput.getMax()));
|
||||||
|
output.green(String.format("Throughput Mean: %.2f", throughput.getMean()));
|
||||||
|
output.green(String.format("Throughput StdDev: %.2f", throughput.getStandardDeviation()));
|
||||||
|
output.green(String.format("Throughput Variance: %.2f", throughput.getVariance()));
|
||||||
|
output.green(
|
||||||
|
String.format(
|
||||||
|
"Mean CPU Usage: %.2f%%", stats.get(LsMetricStats.CPU_USAGE).getMean()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Properties loadSettings() throws IOException {
|
||||||
|
final Properties props = new Properties();
|
||||||
|
try (final InputStream settings =
|
||||||
|
Main.class.getResource("ls-benchmark.properties").openStream()) {
|
||||||
|
props.load(settings);
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package org.logstash.benchmark.cli.cases;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.logstash.benchmark.cli.LogstashInstallation;
|
||||||
|
import org.logstash.benchmark.cli.LsMetricsMonitor;
|
||||||
|
import org.logstash.benchmark.cli.ui.LsMetricStats;
|
||||||
|
import org.logstash.benchmark.cli.util.LsBenchDownloader;
|
||||||
|
import org.openjdk.jmh.util.ListStatistics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case running a set of Apache web-server logs, read from standard input, through the
|
||||||
|
* GeoIP and UserAgent filters and printing the result to standard out using the dots codec.
|
||||||
|
*/
|
||||||
|
public final class ApacheLogsComplex implements Case {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier used by the CLI interface.
|
||||||
|
*/
|
||||||
|
public static final String IDENTIFIER = "apache";
|
||||||
|
|
||||||
|
private final LogstashInstallation logstash;
|
||||||
|
|
||||||
|
private final File data;
|
||||||
|
|
||||||
|
public ApacheLogsComplex(final LogstashInstallation logstash, final Path cwd,
|
||||||
|
final Properties settings) throws IOException, NoSuchAlgorithmException {
|
||||||
|
this.data = cwd.resolve("data_apache").resolve("apache_access_logs").toFile();
|
||||||
|
ensureDatafile(data.toPath().getParent().toFile(), settings);
|
||||||
|
this.logstash = logstash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EnumMap<LsMetricStats, ListStatistics> run() {
|
||||||
|
final LsMetricsMonitor monitor = new LsMetricsMonitor(logstash.metrics());
|
||||||
|
final ExecutorService exec = Executors.newSingleThreadExecutor();
|
||||||
|
final Future<EnumMap<LsMetricStats, ListStatistics>> future = exec.submit(monitor);
|
||||||
|
try {
|
||||||
|
final String config;
|
||||||
|
try (final InputStream cfg = ApacheLogsComplex.class
|
||||||
|
.getResourceAsStream("apache.cfg")) {
|
||||||
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
IOUtils.copy(cfg, baos);
|
||||||
|
config = baos.toString();
|
||||||
|
}
|
||||||
|
logstash.execute(config, data);
|
||||||
|
monitor.stop();
|
||||||
|
return future.get(20L, TimeUnit.SECONDS);
|
||||||
|
} catch (final IOException | InterruptedException | ExecutionException | TimeoutException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
} finally {
|
||||||
|
exec.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ensureDatafile(final File file, final Properties settings)
|
||||||
|
throws IOException, NoSuchAlgorithmException {
|
||||||
|
LsBenchDownloader.downloadDecompress(
|
||||||
|
file, settings.getProperty("org.logstash.benchmark.apache.dataset.url"), false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.logstash.benchmark.cli.cases;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import org.logstash.benchmark.cli.ui.LsMetricStats;
|
||||||
|
import org.openjdk.jmh.util.ListStatistics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definition of an executable test case.
|
||||||
|
*/
|
||||||
|
public interface Case {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the Actual Test Case.
|
||||||
|
* @return Map Containing Test Results
|
||||||
|
*/
|
||||||
|
AbstractMap<LsMetricStats, ListStatistics> run();
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package org.logstash.benchmark.cli.cases;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import org.logstash.benchmark.cli.LogstashInstallation;
|
||||||
|
import org.logstash.benchmark.cli.LsMetricsMonitor;
|
||||||
|
import org.logstash.benchmark.cli.ui.LsMetricStats;
|
||||||
|
import org.openjdk.jmh.util.ListStatistics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs Generator Input and Outputs to StdOut.
|
||||||
|
*/
|
||||||
|
public final class GeneratorToStdout implements Case {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier used by the CLI interface.
|
||||||
|
*/
|
||||||
|
public static final String IDENTIFIER = "baseline";
|
||||||
|
|
||||||
|
private static final String CONFIGURATION =
|
||||||
|
"input { generator { threads => 1 count => 1000000 } } output { stdout { } }";
|
||||||
|
|
||||||
|
private final LogstashInstallation logstash;
|
||||||
|
|
||||||
|
public GeneratorToStdout(final LogstashInstallation logstash) {
|
||||||
|
this.logstash = logstash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EnumMap<LsMetricStats, ListStatistics> run() {
|
||||||
|
final LsMetricsMonitor monitor = new LsMetricsMonitor(logstash.metrics());
|
||||||
|
final ExecutorService exec = Executors.newSingleThreadExecutor();
|
||||||
|
final Future<EnumMap<LsMetricStats, ListStatistics>> future = exec.submit(monitor);
|
||||||
|
try {
|
||||||
|
logstash.execute(GeneratorToStdout.CONFIGURATION);
|
||||||
|
monitor.stop();
|
||||||
|
return future.get(20L, TimeUnit.SECONDS);
|
||||||
|
} catch (final IOException | InterruptedException | ExecutionException | TimeoutException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
} finally {
|
||||||
|
exec.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package org.logstash.benchmark.cli.ui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups of statistics this tool gathers.
|
||||||
|
*/
|
||||||
|
public enum LsMetricStats {
|
||||||
|
/**
|
||||||
|
* Statistics on the event throughput.
|
||||||
|
*/
|
||||||
|
THROUGHPUT,
|
||||||
|
/**
|
||||||
|
* Statistics on the number of events processed overall.
|
||||||
|
*/
|
||||||
|
COUNT,
|
||||||
|
/**
|
||||||
|
* Statistics on CPU usage.
|
||||||
|
*/
|
||||||
|
CPU_USAGE
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.logstash.benchmark.cli.ui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum of the various types of Logstash versions.
|
||||||
|
*/
|
||||||
|
public enum LsVersionType {
|
||||||
|
/**
|
||||||
|
* A local version of Logstash that is assumed to have all dependencies installed and/or build.
|
||||||
|
*/
|
||||||
|
LOCAL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A release version of Logstash to be downloaded from elastic.co mirrors.
|
||||||
|
*/
|
||||||
|
DISTRIBUTION,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A version build from a given GIT tree hash/identifier.
|
||||||
|
*/
|
||||||
|
GIT
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package org.logstash.benchmark.cli.ui;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User Input Definitions and Utility Methods.
|
||||||
|
*/
|
||||||
|
public final class UserInput {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Default Cache/Working-Directory.
|
||||||
|
*/
|
||||||
|
public static final File WORKING_DIRECTORY_DEFAULT = Paths.get(
|
||||||
|
System.getProperty("user.home"), ".logstash-benchmarks"
|
||||||
|
).toFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the testcase to run.
|
||||||
|
*/
|
||||||
|
public static final String TEST_CASE_PARAM = "testcase";
|
||||||
|
|
||||||
|
public static final String TEST_CASE_HELP =
|
||||||
|
"Currently available test cases are 'baseline' and 'apache'.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version parameter to use for Logstash build downloaded from elastic.co.
|
||||||
|
*/
|
||||||
|
public static final String DISTRIBUTION_VERSION_PARAM = "distribution-version";
|
||||||
|
|
||||||
|
public static final String DISTRIBUTION_VERSION_HELP =
|
||||||
|
"The version of a Logstash build to download from elastic.co.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version parameter to use for Logstash build form a Git has.
|
||||||
|
*/
|
||||||
|
public static final String GIT_VERSION_PARAM = "git-hash";
|
||||||
|
|
||||||
|
public static final String GIT_VERSION_HELP = String.join(
|
||||||
|
"\n",
|
||||||
|
"Either a git tree (tag/branch or commit hash), optionally prefixed by a Github username,",
|
||||||
|
"if ran against forks.",
|
||||||
|
"E.g. 'ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7',",
|
||||||
|
"'user-name#ab1cfe8cf7e20114df58bcc6c996abcb2b0650d7' or 'master'"
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final String LOCAL_VERSION_PARAM = "local-path";
|
||||||
|
|
||||||
|
public static final String LOCAL_VERSION_HELP =
|
||||||
|
"Path to the root of a local Logstash distribution.\n E.g. `/opt/logstash`";
|
||||||
|
|
||||||
|
public static final String WORKING_DIRECTORY_PARAM = "workdir";
|
||||||
|
|
||||||
|
public static final String WORKING_DIRECTORY_HELP =
|
||||||
|
"Working directory to store cached files in.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
private UserInput() {
|
||||||
|
// Utility Class
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package org.logstash.benchmark.cli.ui;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
|
|
||||||
|
public final class UserOutput {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder()
|
||||||
|
.append(DateTimeFormatter.ofPattern("E")).appendLiteral(' ')
|
||||||
|
.append(DateTimeFormatter.ofPattern("L")).appendLiteral(' ')
|
||||||
|
.append(DateTimeFormatter.ofPattern("d")).appendLiteral(' ')
|
||||||
|
.append(DateTimeFormatter.ISO_LOCAL_TIME).appendLiteral(' ')
|
||||||
|
.append(DateTimeFormatter.ofPattern("yyyy")).appendLiteral(' ')
|
||||||
|
.append(DateTimeFormatter.ofPattern("z")).toFormatter();
|
||||||
|
|
||||||
|
private final PrintStream target;
|
||||||
|
|
||||||
|
public UserOutput(final PrintStream target) {
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printStartTime() {
|
||||||
|
green(
|
||||||
|
String.format(
|
||||||
|
"Start Time: %s", DATE_TIME_FORMATTER.format(ZonedDateTime.now().withNano(0))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printLine() {
|
||||||
|
green("------------------------------------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printBanner() {
|
||||||
|
green(
|
||||||
|
"██╗ ██████╗ ██████╗ ███████╗████████╗ █████╗ ███████╗██╗ ██╗ \n" +
|
||||||
|
"██║ ██╔═══██╗██╔════╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██║ \n" +
|
||||||
|
"██║ ██║ ██║██║ ███╗███████╗ ██║ ███████║███████╗███████║ \n" +
|
||||||
|
"██║ ██║ ██║██║ ██║╚════██║ ██║ ██╔══██║╚════██║██╔══██║ \n" +
|
||||||
|
"███████╗╚██████╔╝╚██████╔╝███████║ ██║ ██║ ██║███████║██║ ██║ \n" +
|
||||||
|
"╚══════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ \n" +
|
||||||
|
" \n" +
|
||||||
|
"██████╗ ███████╗███╗ ██╗ ██████╗██╗ ██╗███╗ ███╗ █████╗ ██████╗ ██╗ ██╗\n" +
|
||||||
|
"██╔══██╗██╔════╝████╗ ██║██╔════╝██║ ██║████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝\n" +
|
||||||
|
"██████╔╝█████╗ ██╔██╗ ██║██║ ███████║██╔████╔██║███████║██████╔╝█████╔╝ \n" +
|
||||||
|
"██╔══██╗██╔══╝ ██║╚██╗██║██║ ██╔══██║██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗ \n" +
|
||||||
|
"██████╔╝███████╗██║ ╚████║╚██████╗██║ ██║██║ ╚═╝ ██║██║ ██║██║ ██║██║ ██╗\n" +
|
||||||
|
"╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝\n" +
|
||||||
|
" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void green(final String line) {
|
||||||
|
target.println(colorize(line, "\u001B[32m"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String colorize(final String line, final String prefix) {
|
||||||
|
final String reset = "\u001B[0m";
|
||||||
|
return new StringBuilder(line.length() + 2 * reset.length())
|
||||||
|
.append(prefix).append(line).append(reset).toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package org.logstash.benchmark.cli.util;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||||
|
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
||||||
|
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
|
||||||
|
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
||||||
|
import org.apache.commons.compress.utils.IOUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for decompressing archives.
|
||||||
|
*/
|
||||||
|
final class LsBenchCompressUtil {
|
||||||
|
|
||||||
|
private LsBenchCompressUtil() {
|
||||||
|
// Utility Class
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unzipDir(final String zipFile, final File folder) throws IOException {
|
||||||
|
if (!folder.exists() && !folder.mkdir()) {
|
||||||
|
throw new IllegalStateException("unzip failed");
|
||||||
|
}
|
||||||
|
try (ArchiveInputStream zis = new ZipArchiveInputStream(new FileInputStream(zipFile))) {
|
||||||
|
unpackDir(folder, zis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void gunzipDir(final File gzfile, final File file) throws IOException {
|
||||||
|
final File ball =
|
||||||
|
file.toPath().getParent().resolve(String.valueOf(UUID.randomUUID())).toFile();
|
||||||
|
gunzipFile(gzfile, ball);
|
||||||
|
try (final TarArchiveInputStream tar = new TarArchiveInputStream(
|
||||||
|
new FileInputStream(ball))) {
|
||||||
|
unpackDir(file, tar);
|
||||||
|
}
|
||||||
|
LsBenchFileUtil.ensureDeleted(ball);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void unpackDir(final File destination, final ArchiveInputStream archive)
|
||||||
|
throws IOException {
|
||||||
|
ArchiveEntry entry = archive.getNextEntry();
|
||||||
|
while (entry != null) {
|
||||||
|
final File newFile =
|
||||||
|
Paths.get(destination.getAbsolutePath(), entry.getName()).toFile();
|
||||||
|
if (!newFile.getParentFile().exists() && !newFile.getParentFile().mkdirs()) {
|
||||||
|
throw new IllegalStateException("unzip failed");
|
||||||
|
}
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
if (!newFile.exists() && !newFile.mkdir()) {
|
||||||
|
throw new IllegalStateException("unzip failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try (final FileOutputStream fos = new FileOutputStream(newFile)) {
|
||||||
|
IOUtils.copy(archive, fos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry = archive.getNextEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void gunzipFile(final File gzfile, final File file) throws IOException {
|
||||||
|
try (
|
||||||
|
final FileOutputStream uncompressed = new FileOutputStream(file);
|
||||||
|
final InputStream archive = new GzipCompressorInputStream(
|
||||||
|
new BufferedInputStream(new FileInputStream(gzfile)))) {
|
||||||
|
IOUtils.copy(archive, uncompressed);
|
||||||
|
}
|
||||||
|
LsBenchFileUtil.ensureDeleted(gzfile);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.logstash.benchmark.cli.util;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import org.apache.commons.compress.compressors.gzip.GzipUtils;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
|
||||||
|
public final class LsBenchDownloader {
|
||||||
|
|
||||||
|
public static void downloadDecompress(final File file, final String url, final boolean force)
|
||||||
|
throws IOException, NoSuchAlgorithmException {
|
||||||
|
if (force && file.exists()) {
|
||||||
|
LsBenchFileUtil.ensureDeleted(file);
|
||||||
|
}
|
||||||
|
if (!file.exists()) {
|
||||||
|
final File temp = file.getParentFile().toPath().resolve(
|
||||||
|
String.format("%s.download", file.getName())).toFile();
|
||||||
|
LsBenchFileUtil.ensureDeleted(temp);
|
||||||
|
try (final OutputStream target = new BufferedOutputStream(new FileOutputStream(temp))) {
|
||||||
|
try (final CloseableHttpClient client = HttpClientBuilder.create().setSSLContext(
|
||||||
|
SSLContext.getDefault()).build()) {
|
||||||
|
try (final CloseableHttpResponse response = client
|
||||||
|
.execute(new HttpGet(url))) {
|
||||||
|
response.getEntity().writeTo(target);
|
||||||
|
}
|
||||||
|
target.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (GzipUtils.isCompressedFilename(url)) {
|
||||||
|
LsBenchCompressUtil.gunzipDir(temp, file);
|
||||||
|
}
|
||||||
|
if (url.endsWith(".zip")) {
|
||||||
|
LsBenchCompressUtil.unzipDir(temp.getAbsolutePath(), file);
|
||||||
|
}
|
||||||
|
LsBenchFileUtil.ensureDeleted(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.logstash.benchmark.cli.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for file handling.
|
||||||
|
*/
|
||||||
|
public final class LsBenchFileUtil {
|
||||||
|
|
||||||
|
private LsBenchFileUtil() {
|
||||||
|
//Utility Class
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ensureDeleted(final File file) throws IOException {
|
||||||
|
if (file.exists()) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
FileUtils.deleteDirectory(file);
|
||||||
|
} else {
|
||||||
|
if (!file.delete()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
String.format("Failed to delete %s", file.getAbsolutePath())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ensureExecutable(final File file) {
|
||||||
|
if (!file.canExecute() && !file.setExecutable(true)) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
String.format("Failed to set %s executable", file.getAbsolutePath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.logstash.benchmark.cli.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import org.logstash.benchmark.cli.JRubyInstallation;
|
||||||
|
import org.logstash.benchmark.cli.LogstashInstallation;
|
||||||
|
import org.logstash.benchmark.cli.ui.LsVersionType;
|
||||||
|
|
||||||
|
public final class LsBenchLsSetup {
|
||||||
|
|
||||||
|
private LsBenchLsSetup() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogstashInstallation logstashFromGit(final String pwd, final String version,
|
||||||
|
final JRubyInstallation jruby) {
|
||||||
|
final File lsdir = Paths.get(pwd, "logstash").toFile();
|
||||||
|
final LogstashInstallation logstash;
|
||||||
|
if (version.contains("#")) {
|
||||||
|
final String[] parts = version.split("#");
|
||||||
|
logstash = new LogstashInstallation.FromGithub(lsdir, parts[0], parts[1], jruby);
|
||||||
|
} else {
|
||||||
|
logstash = new LogstashInstallation.FromGithub(lsdir, version, jruby);
|
||||||
|
}
|
||||||
|
return logstash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogstashInstallation setupLS(final String pwd, final String version,
|
||||||
|
final LsVersionType type) {
|
||||||
|
final LogstashInstallation logstash;
|
||||||
|
if (type == LsVersionType.LOCAL) {
|
||||||
|
logstash = new LogstashInstallation.FromLocalPath(version);
|
||||||
|
} else {
|
||||||
|
logstash = new LogstashInstallation.FromRelease(
|
||||||
|
Paths.get(pwd, String.format("ls-release-%s", version)).toFile(), version
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return logstash;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
input {
|
||||||
|
stdin { }
|
||||||
|
}
|
||||||
|
|
||||||
|
filter {
|
||||||
|
grok {
|
||||||
|
match => {
|
||||||
|
"message" => '%{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "%{WORD:verb} %{DATA:request} HTTP/%{NUMBER:httpversion}" %{NUMBER:response:int} (?:-|%{NUMBER:bytes:int}) %{QS:referrer} %{QS:agent}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
date {
|
||||||
|
match => [ "timestamp", "dd/MMM/YYYY:HH:mm:ss Z" ]
|
||||||
|
locale => en
|
||||||
|
}
|
||||||
|
|
||||||
|
geoip {
|
||||||
|
source => "clientip"
|
||||||
|
}
|
||||||
|
|
||||||
|
useragent {
|
||||||
|
source => "agent"
|
||||||
|
target => "useragent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
stdout { codec => dots }
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.logstash.benchmark.apache.dataset.url=https://s3.amazonaws.com/data.elasticsearch.org/apache_logs/apache_access_logs.tar.gz
|
|
@ -0,0 +1,76 @@
|
||||||
|
package org.logstash.benchmark.cli;
|
||||||
|
|
||||||
|
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||||
|
import com.github.tomakehurst.wiremock.junit.WireMockRule;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.hamcrest.CoreMatchers;
|
||||||
|
import org.hamcrest.MatcherAssert;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.logstash.benchmark.cli.ui.LsMetricStats;
|
||||||
|
import org.openjdk.jmh.util.ListStatistics;
|
||||||
|
import org.openjdk.jmh.util.Statistics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link LsMetricsMonitor}.
|
||||||
|
*/
|
||||||
|
public final class LsMetricsMonitorTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public WireMockRule http = new WireMockRule(0);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsesFilteredCount() throws Exception {
|
||||||
|
final String path = "/_node/stats/?pretty";
|
||||||
|
http.stubFor(WireMock.get(WireMock.urlEqualTo(path)).willReturn(WireMock.okJson(
|
||||||
|
new String(
|
||||||
|
Files.readAllBytes(
|
||||||
|
Paths.get(LsMetricsMonitorTest.class.getResource("metrics.json").getPath()
|
||||||
|
))
|
||||||
|
, StandardCharsets.UTF_8)
|
||||||
|
)));
|
||||||
|
final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
try {
|
||||||
|
final LsMetricsMonitor monitor =
|
||||||
|
new LsMetricsMonitor(String.format("http://127.0.0.1:%d/%s", http.port(), path));
|
||||||
|
final Future<EnumMap<LsMetricStats, ListStatistics>> future = executor.submit(monitor);
|
||||||
|
TimeUnit.SECONDS.sleep(5L);
|
||||||
|
monitor.stop();
|
||||||
|
final Statistics stats = future.get().get(LsMetricStats.THROUGHPUT);
|
||||||
|
MatcherAssert.assertThat(stats.getMax(), CoreMatchers.is(21052.0D));
|
||||||
|
} finally {
|
||||||
|
executor.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parsesCpuUsage() throws Exception {
|
||||||
|
final String path = "/_node/stats/?pretty";
|
||||||
|
http.stubFor(WireMock.get(WireMock.urlEqualTo(path)).willReturn(WireMock.okJson(
|
||||||
|
new String(
|
||||||
|
Files.readAllBytes(
|
||||||
|
Paths.get(LsMetricsMonitorTest.class.getResource("metrics.json").getPath()
|
||||||
|
))
|
||||||
|
, StandardCharsets.UTF_8)
|
||||||
|
)));
|
||||||
|
final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
try {
|
||||||
|
final LsMetricsMonitor monitor =
|
||||||
|
new LsMetricsMonitor(String.format("http://127.0.0.1:%d/%s", http.port(), path));
|
||||||
|
final Future<EnumMap<LsMetricStats, ListStatistics>> future = executor.submit(monitor);
|
||||||
|
TimeUnit.SECONDS.sleep(5L);
|
||||||
|
monitor.stop();
|
||||||
|
final Statistics stats = future.get().get(LsMetricStats.CPU_USAGE);
|
||||||
|
MatcherAssert.assertThat(stats.getMax(), CoreMatchers.is(63.0D));
|
||||||
|
} finally {
|
||||||
|
executor.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package org.logstash.benchmark.cli;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
import org.logstash.benchmark.cli.ui.UserInput;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link Main}.
|
||||||
|
* todo: These tests are ignored for now, their runtime is simply unreasonable for any CI scenario.
|
||||||
|
* We will have to find a reasonable trade-off here for making sure the benchmark code is functional
|
||||||
|
* without increasing test runtime by many minutes.
|
||||||
|
*/
|
||||||
|
public final class MainTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final TemporaryFolder temp = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void downloadsDependenciesForGithub() throws Exception {
|
||||||
|
final File pwd = temp.newFolder();
|
||||||
|
Main.main(String.format("--workdir=%s", pwd.getAbsolutePath()));
|
||||||
|
final Path logstash = pwd.toPath().resolve("logstash").resolve("logstash-master");
|
||||||
|
assertThat(logstash.toFile().exists(), is(true));
|
||||||
|
final File jruby = pwd.toPath().resolve("jruby").toFile();
|
||||||
|
assertThat(jruby.exists(), is(true));
|
||||||
|
assertThat(jruby.isDirectory(), is(true));
|
||||||
|
assertThat(logstash.resolve("Gemfile").toFile().exists(), is(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception On Failure
|
||||||
|
* @todo cleanup path here, works though if you plug in a correct path
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void runsAgainstLocal() throws Exception {
|
||||||
|
final File pwd = temp.newFolder();
|
||||||
|
Main.main(String.format(
|
||||||
|
"--version=local:%s",
|
||||||
|
System.getProperty("logstash.benchmark.test.local.path")
|
||||||
|
), String.format("--workdir=%s", pwd.getAbsolutePath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception On Failure
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void runsAgainstRelease() throws Exception {
|
||||||
|
final File pwd = temp.newFolder();
|
||||||
|
Main.main(
|
||||||
|
String.format("--%s=5.5.0", UserInput.DISTRIBUTION_VERSION_PARAM),
|
||||||
|
String.format("--workdir=%s", pwd.getAbsolutePath())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception On Failure
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void runsApacheAgainstRelease() throws Exception {
|
||||||
|
final File pwd = temp.newFolder();
|
||||||
|
Main.main(
|
||||||
|
String.format("--%s=5.5.0", UserInput.DISTRIBUTION_VERSION_PARAM),
|
||||||
|
String.format("--%s=apache", UserInput.TEST_CASE_PARAM),
|
||||||
|
String.format("--workdir=%s", pwd.getAbsolutePath())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
{
|
||||||
|
"host": "localhost",
|
||||||
|
"version": "6.0.0-alpha3",
|
||||||
|
"http_address": "127.0.0.1:9600",
|
||||||
|
"id": "8bbabc13-ea58-4dcd-b94e-90ae5f692c17",
|
||||||
|
"name": "localhost",
|
||||||
|
"jvm": {
|
||||||
|
"threads": {
|
||||||
|
"count": 28,
|
||||||
|
"peak_count": 28
|
||||||
|
},
|
||||||
|
"mem": {
|
||||||
|
"heap_used_percent": 16,
|
||||||
|
"heap_committed_in_bytes": 259522560,
|
||||||
|
"heap_max_in_bytes": 1037959168,
|
||||||
|
"heap_used_in_bytes": 168360000,
|
||||||
|
"non_heap_used_in_bytes": 113241032,
|
||||||
|
"non_heap_committed_in_bytes": 124989440,
|
||||||
|
"pools": {
|
||||||
|
"survivor": {
|
||||||
|
"peak_used_in_bytes": 8912896,
|
||||||
|
"used_in_bytes": 6872400,
|
||||||
|
"peak_max_in_bytes": 35782656,
|
||||||
|
"max_in_bytes": 35782656,
|
||||||
|
"committed_in_bytes": 8912896
|
||||||
|
},
|
||||||
|
"old": {
|
||||||
|
"peak_used_in_bytes": 141395984,
|
||||||
|
"used_in_bytes": 119128832,
|
||||||
|
"peak_max_in_bytes": 715849728,
|
||||||
|
"max_in_bytes": 715849728,
|
||||||
|
"committed_in_bytes": 178978816
|
||||||
|
},
|
||||||
|
"young": {
|
||||||
|
"peak_used_in_bytes": 71630848,
|
||||||
|
"used_in_bytes": 42358768,
|
||||||
|
"peak_max_in_bytes": 286326784,
|
||||||
|
"max_in_bytes": 286326784,
|
||||||
|
"committed_in_bytes": 71630848
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gc": {
|
||||||
|
"collectors": {
|
||||||
|
"old": {
|
||||||
|
"collection_time_in_millis": 89,
|
||||||
|
"collection_count": 3
|
||||||
|
},
|
||||||
|
"young": {
|
||||||
|
"collection_time_in_millis": 516,
|
||||||
|
"collection_count": 36
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uptime_in_millis": 15055
|
||||||
|
},
|
||||||
|
"process": {
|
||||||
|
"open_file_descriptors": 63,
|
||||||
|
"peak_open_file_descriptors": 63,
|
||||||
|
"max_file_descriptors": 10240,
|
||||||
|
"mem": {
|
||||||
|
"total_virtual_in_bytes": 5335916544
|
||||||
|
},
|
||||||
|
"cpu": {
|
||||||
|
"total_in_millis": 67919,
|
||||||
|
"percent": 63,
|
||||||
|
"load_average": {
|
||||||
|
"1m": 2.6826171875
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"in": 23101,
|
||||||
|
"filtered": 21052,
|
||||||
|
"out": 21052,
|
||||||
|
"duration_in_millis": 8939,
|
||||||
|
"queue_push_duration_in_millis": 3978
|
||||||
|
},
|
||||||
|
"pipelines": {
|
||||||
|
"main": {
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 9250,
|
||||||
|
"in": 24125,
|
||||||
|
"filtered": 22076,
|
||||||
|
"out": 22076,
|
||||||
|
"queue_push_duration_in_millis": 4236
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-1",
|
||||||
|
"events": {
|
||||||
|
"out": 24125,
|
||||||
|
"queue_push_duration_in_millis": 4236
|
||||||
|
},
|
||||||
|
"name": "stdin"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-4",
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 374,
|
||||||
|
"in": 23045,
|
||||||
|
"out": 23044
|
||||||
|
},
|
||||||
|
"name": "geoip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-3",
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 24,
|
||||||
|
"in": 23045,
|
||||||
|
"out": 23045
|
||||||
|
},
|
||||||
|
"matches": 23045,
|
||||||
|
"name": "date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-5",
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 1373,
|
||||||
|
"in": 23045,
|
||||||
|
"out": 23045
|
||||||
|
},
|
||||||
|
"name": "useragent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-2",
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 295,
|
||||||
|
"in": 23047,
|
||||||
|
"out": 23045
|
||||||
|
},
|
||||||
|
"matches": 23045,
|
||||||
|
"patterns_per_field": {
|
||||||
|
"message": 1
|
||||||
|
},
|
||||||
|
"name": "grok"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"id": "1db6e3e8163d4cf302e5b5ee12f6fc3dcfe783ba-6",
|
||||||
|
"events": {
|
||||||
|
"duration_in_millis": 89,
|
||||||
|
"in": 22076,
|
||||||
|
"out": 22076
|
||||||
|
},
|
||||||
|
"name": "stdout"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"reloads": {
|
||||||
|
"last_error": null,
|
||||||
|
"successes": 0,
|
||||||
|
"last_success_timestamp": null,
|
||||||
|
"last_failure_timestamp": null,
|
||||||
|
"failures": 0
|
||||||
|
},
|
||||||
|
"queue": {
|
||||||
|
"type": "memory"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"reloads": {
|
||||||
|
"successes": 0,
|
||||||
|
"failures": 0
|
||||||
|
},
|
||||||
|
"os": {}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue