Skip to content

Commit 1ea164f

Browse files
author
Martin Herrman
committed
Initial release
1 parent 31c587b commit 1ea164f

File tree

12 files changed

+2226
-7
lines changed

12 files changed

+2226
-7
lines changed

.github/workflows/build.yml

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: build x2i
2+
3+
on:
4+
push:
5+
tags:
6+
- x2i-*
7+
8+
workflow_dispatch:
9+
10+
jobs:
11+
build:
12+
13+
runs-on: ubuntu-latest
14+
permissions:
15+
contents: write
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Go
20+
uses: actions/setup-go@v5
21+
with:
22+
go-version: 1.22.4
23+
24+
- name: Build
25+
run: |
26+
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o x2i-linux-amd64 x2i.go
27+
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o x2i-linux-arm64 x2i.go
28+
GOOS=windows GOARCH=386 go build -o x2i-windows-386.exe x2i.go
29+
GOOS=windows GOARCH=amd64 go build -o x2i-windows-amd64.exe x2i.go
30+
GOOS=darwin GOARCH=amd64 go build -o x2i-macos-amd64 x2i.go
31+
GOOS=darwin GOARCH=arm64 go build -o x2i-macos-arm64 x2i.go
32+
33+
- uses: olegtarasov/[email protected]
34+
id: tagName
35+
36+
- uses: ncipollo/[email protected]
37+
with:
38+
artifacts: "x2i-*"
39+
body: "Release ${{ steps.tagName.outputs.tag }}"
40+

LICENSE

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
MIT License
1+
The MIT License (MIT)
22

3-
Copyright (c) 2024 Perfana
3+
Copyright © 2020 Anton Kramarev
4+
Copyright © 2024 Perfana Software B.V.
45

56
Permission is hereby granted, free of charge, to any person obtaining a copy
67
of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +10,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
910
copies of the Software, and to permit persons to whom the Software is
1011
furnished to do so, subject to the following conditions:
1112

12-
The above copyright notice and this permission notice shall be included in all
13-
copies or substantial portions of the Software.
13+
The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
1415

1516
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1617
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1718
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1819
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1920
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21-
SOFTWARE.
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
THE SOFTWARE.

README.md

+100-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,101 @@
11
# x2i
2-
Write Gatling, JMeter or K6 logs directly to InfluxDB.
2+
3+
## What is x2i?
4+
x2i sends raw data from Gatling, JMeter and K6 load generation tools to
5+
[InfluxDB time series database](https://www.influxdata.com/). x2i is the default solution for Perfana customers to
6+
send load generation tool results to Perfana and use this data to automatically analyse performance tests. x2i can
7+
also be used independently of Perfana.
8+
9+
## History
10+
x2i evolved from Anton Kramarev's [gatling-to-influxdb](https://github.com/Dakaraj/gatling-to-influxdb), a project
11+
that processes the Gatling log file and sends the raw data to influxdb for analysis. Anton's goal was to replace the
12+
Graphite backend plugin in Gatling that allows aggregated data to send to influxdb. Using raw data allows for more
13+
detailed analysis of test results using Influxdb.
14+
15+
At Perfana we forked the project, added support for additional load generation tools, and made several other
16+
improvements. Hence the name 'x2i', where the 'x' stands for multiple load generation tools.
17+
18+
## Need support?
19+
Existing Perfana customers and trial users best contact Perfana directly in case of any questions or issues. If you
20+
are not (yet ;-) a Perfana customer or trial user, feel free to create an issue in this Github repository. Please limit
21+
an issue to x2i specifically, we won't be able to troubleshoot your custom integration.
22+
23+
## Supported platforms
24+
Binaries for multiple operating systems and architectures are provided. In production x2i is mostly used on
25+
AMD64 Linux. Our development systems also include MacOS on ARM. Other platforms are used sporadically and could have
26+
unknown issues.
27+
28+
## Influxdb requirements
29+
x2i requires an existing database with read/write access. The read access is used to verify the connection to the
30+
database.
31+
32+
The following versions of InfluxDB are known to work:
33+
* InfluxDB OSS 1.8.10
34+
35+
36+
37+
## Usage
38+
Run `x2i -h` for a quick help with examples for the different load generation tools that are supported.
39+
40+
### Generic
41+
x2i needs only one argument, the location where to find the results of the testrun. In a common scenario you will
42+
provide additional arguments for:
43+
* the address, database, username and password for influxdb
44+
* the test tool that is used; this can be omitted if gatling is used
45+
46+
The `--system-under-test` and `--test-environment` arguments could be used, and are required for the Perfana integration
47+
to work properly.
48+
49+
Datapoints are send to Influxdb when either the hardcoded timeout of 5 seconds is reached, or when `--max-batch-size`
50+
number of datapoints is logged. The number of datapoints that is being send is logged in the x2i log file.
51+
52+
### Gatling
53+
54+
### JMeter
55+
56+
### K6
57+
58+
## Integration
59+
60+
### Perfana
61+
When using x2i with Perfana, x2i will be started using a CommandRunnerEventConfig in your POM. In the starter
62+
packages for gatling, jmeter and k6, you will find a perfect example. The `--system-under-test` and `--test-environment`
63+
arguments are required for the Perfana integration to work properly.
64+
65+
### CI/CD
66+
You could integrate x2i in your CI/CD pipeline. When running in detached mode, x2i will return the PID as follows:
67+
68+
```bash
69+
[PID] 20201
70+
```
71+
72+
allowing you to stop the process when the test has finished.
73+
74+
```bash
75+
echo "Starting x2i in detached mode, saving PID in variable" && \
76+
x2iPID=$(x2i <arguments> -d | awk '{print $2}') && \
77+
echo "Build and execute test" && \
78+
<run your test> \
79+
echo "Waiting for parser to safely finish all its work" && \
80+
sleep 30 && \
81+
echo "Sending interrupt signal to x2i process" && \
82+
kill -INT $x2iPID && \
83+
echo "Waiting for process to stop safely" && \
84+
sleep 30 && \
85+
echo "Exiting"
86+
```
87+
88+
## Building application
89+
Building the application should be straightforward. Install the go language development tools and run `go build`.
90+
91+
## Want to contribute?
92+
We welcome contributions from users of x2i. If you would like to contribute to this project, we encourage you to
93+
create an issue to discuss the contribution first.
94+
95+
## How to release new version?
96+
* Commit your changes to main.
97+
* Make sure you update the version number in cmd/root.go, around line 96.
98+
* Create new tag with the new version, this triggers a new build and published the binaries.
99+
100+
## License
101+
This application is licensed under MIT license. Please read the LICENSE file for details.

cmd/root.go

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
Copyright © 2020 Anton Kramarev
3+
Copyright © 2024 Perfana Software B.V.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.
22+
*/
23+
24+
package cmd
25+
26+
import (
27+
"context"
28+
"fmt"
29+
"log"
30+
"os"
31+
"os/exec"
32+
"os/signal"
33+
"syscall"
34+
35+
"github.com/perfana/x2i/influx"
36+
l "github.com/perfana/x2i/logger"
37+
"github.com/perfana/x2i/gatlingparser"
38+
"github.com/perfana/x2i/jmeterparser"
39+
"github.com/perfana/x2i/k6parser"
40+
"github.com/spf13/cobra"
41+
)
42+
43+
var (
44+
ctx context.Context
45+
cancel context.CancelFunc
46+
)
47+
48+
func preRunSetup(cmd *cobra.Command, args []string) error {
49+
50+
// Check if InfluxDB connection is successful before going to detached mode
51+
err := influx.InitInfluxConnection(cmd)
52+
if err != nil {
53+
return fmt.Errorf("Failed to establish successful database connection: %w", err)
54+
}
55+
56+
// If detached state is requested, filter out corresponding flags and start new process
57+
// returning with same arguments printing its PID. Then close the initial process
58+
if d, _ := cmd.Flags().GetBool("detached"); d {
59+
newArgs := make([]string, 0, len(os.Args)-2)
60+
for _, a := range os.Args[1:] {
61+
if a == "-d" || a == "--detached" {
62+
continue
63+
}
64+
newArgs = append(newArgs, a)
65+
}
66+
67+
command := exec.Command(os.Args[0], newArgs...)
68+
if err := command.Start(); err != nil {
69+
return fmt.Errorf("Failed to start a detached process: %w", err)
70+
}
71+
pid := command.Process.Pid
72+
fmt.Printf("[PID]\t%d\n", pid)
73+
os.Exit(0)
74+
}
75+
76+
// catcher of SIGINT SIGTERM signals
77+
go func() {
78+
c := make(chan os.Signal, 1)
79+
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
80+
sig := <-c
81+
l.Infof("Received signal %v. Stopping application...\n", sig)
82+
cancel()
83+
}()
84+
85+
l.Infoln("Starting application...")
86+
87+
return nil
88+
}
89+
90+
// rootCmd represents the base command when called without any subcommands
91+
var rootCmd = &cobra.Command{
92+
Use: "x2i [path/to/results/dir]",
93+
Example: "x2i [path]/target/gatling -i gatling -d -b gatling\nx2i [path]/target/jmeter/results -i jmeter -d -b jmeter\nx2i [path] -i k6 -d -b k6",
94+
Short: "\nWrite Gatling, JMeter or K6 logs directly to InfluxDB.\n\nMore info at https://github.com/perfana/x2i",
95+
Version: "v1.0.0",
96+
PreRunE: preRunSetup,
97+
Args: cobra.ExactArgs(1),
98+
Run: func(cmd *cobra.Command, args []string) {
99+
i, _ := cmd.Flags().GetString("testtool")
100+
if i == "jmeter" {
101+
jmeterparser.RunMain(cmd, args[0])
102+
} else if i == "k6" {
103+
k6parser.RunMain(cmd, args[0])
104+
} else {
105+
gatlingparser.RunMain(cmd, args[0])
106+
}
107+
},
108+
}
109+
110+
// Execute adds all child commands to the root command and sets flags appropriately.
111+
// This is called by main.main(). It only needs to happen once to the rootCmd.
112+
func Execute() {
113+
// Initiating logger before any other processes start
114+
logPath, _ := rootCmd.Flags().GetString("log")
115+
err := l.InitLogger(logPath)
116+
if err != nil {
117+
log.Fatalf("Failed to init application logger: %v\n", err)
118+
}
119+
120+
if err := rootCmd.ExecuteContext(ctx); err != nil {
121+
l.Errorln(err)
122+
os.Exit(1)
123+
}
124+
}
125+
126+
func init() {
127+
rootCmd.Flags().BoolP("help", "h", false, "Display this help for x2i application")
128+
rootCmd.Flags().BoolP("version", "v", false, "Display current x2i application version")
129+
rootCmd.Flags().BoolP("detached", "d", false, "Run application in background. Returns [PID] on start")
130+
rootCmd.Flags().StringP("address", "a", "http://localhost:8086", "HTTP address and port of InfluxDB instance")
131+
rootCmd.Flags().StringP("username", "u", "", "Username credential for InfluxDB instance")
132+
rootCmd.Flags().StringP("password", "p", "", "Password credential for InfluxDB instance")
133+
rootCmd.Flags().StringP("database", "b", "", "Database name in InfluxDB")
134+
rootCmd.Flags().StringP("testtool", "i", "gatling", "Testtool used, can be gatling, jmeter or k6")
135+
rootCmd.Flags().StringP("log", "l", "x2i.log", "File path to x2i log file")
136+
rootCmd.Flags().StringP("test-environment", "t", "", "Test environment identifier")
137+
rootCmd.Flags().StringP("system-under-test", "y", "", "System under test identifier")
138+
rootCmd.Flags().UintP("stop-timeout", "s", 120, "Time (seconds) to exit if no new log lines found")
139+
rootCmd.Flags().UintP("max-batch-size", "m", 1000, "Max points batch size to sent to InfluxDB")
140+
141+
// set up global context
142+
ctx, cancel = context.WithCancel(context.Background())
143+
}

0 commit comments

Comments
 (0)