Skip to content

Commit 4c88be9

Browse files
authored
Merge pull request #20 from pjcalvo/release-1.0
fixed succes error + renamed tags to extra tags + make db creation att…
2 parents 48102d7 + d3688a2 commit 4c88be9

File tree

12 files changed

+264
-49
lines changed

12 files changed

+264
-49
lines changed

.github/workflows/python-publish.yml

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# This workflow will upload a Python Package using Twine when a release is created
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
3+
4+
# This workflow uses actions that are not certified by GitHub.
5+
# They are provided by a third-party and are governed by
6+
# separate terms of service, privacy policy, and support
7+
# documentation.
8+
9+
name: Upload Python Package
10+
11+
on:
12+
release:
13+
types: [published]
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
deploy:
20+
21+
runs-on: ubuntu-latest
22+
23+
steps:
24+
- uses: actions/checkout@v3
25+
- name: Set up Python
26+
uses: actions/setup-python@v3
27+
with:
28+
python-version: '3.x'
29+
- name: Install dependencies
30+
run: |
31+
python -m pip install --upgrade pip
32+
pip install build
33+
- name: Build package
34+
run: python -m build
35+
- name: Publish package
36+
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
37+
with:
38+
user: __token__
39+
password: ${{ secrets.PYPI_API_TOKEN }}

.github/workflows/python-test.yml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3+
4+
name: Python package
5+
6+
on:
7+
push:
8+
branches: [ "main" ]
9+
pull_request:
10+
branches: [ "main" ]
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
python-version: ["3.9", "3.10", "3.11"]
20+
21+
steps:
22+
- uses: actions/checkout@v3
23+
- name: Set up Python ${{ matrix.python-version }}
24+
uses: actions/setup-python@v3
25+
with:
26+
python-version: ${{ matrix.python-version }}
27+
- name: Install dependencies
28+
run: |
29+
python -m pip install --upgrade pip
30+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
31+
- name: Test with pytest
32+
run: |
33+
python test_suite.py

README.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,16 @@ def on_locust_init(environment, **_kwargs):
3232
"""
3333
# this settings matches the given docker-compose file
3434
influxDBSettings = InfluxDBSettings(
35-
influx_host = 'localhost',
36-
influx_port = '8086',
35+
host = 'localhost',
36+
port = '8086',
3737
user = 'admin',
3838
pwd = 'pass',
3939
database = 'test-project'
4040

4141
# optional global tags to be added to each metric sent to influxdb
42-
tags = {
43-
'some-global-tag': 'some-global-tag-value',
42+
additional_tags = {
43+
'environment': 'test',
44+
'some_other_tag': 'tag_value',
4445
}
4546
)
4647
# start listerner with the given configuration

example/locust.conf

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
locustfile = locustfiles/locustfile.py # file to run
22
headless = true # to run with the locust UI
3-
host = https://hoodoo.digital # base url for the domains
3+
host = https://pjcalvo.github.io # base url for the domains
44
users = 5 # amout of users that will run
55
master = false # to set master/worker state
66
spawn-rate = 1 # spawn rate per second

example/locustfiles/locustfile.py

+13-11
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ def on_locust_init(environment, **_kwargs):
1010
"""
1111
# this settings matches the given docker-compose file
1212
influxDBSettings = InfluxDBSettings(
13-
influx_host = 'localhost',
14-
influx_port = '8086',
13+
host = 'localhost',
14+
port = '8086',
1515
user = 'admin',
1616
pwd = 'pass',
1717
database = 'test-project',
1818

19-
# optional global tags to be added to each metric sent to influxdb
20-
tags = {
21-
'some-global-tag': 'some-global-tag-value',
19+
# additional_tags tags to be added to each metric sent to influxdb
20+
additional_tags = {
21+
'environment': 'test',
22+
'some_other_tag': 'tag_value',
2223
}
2324
)
2425
# start listerner with the given configuration
@@ -35,14 +36,15 @@ def home_page(self):
3536
if response.status_code != 200:
3637
response.failure("Got wrong response")
3738

38-
@tag('connectors')
39+
# purposely fail finding the text
40+
@tag('about_page')
3941
@task(1)
40-
def workfront_connector(self):
41-
with self.client.get("/connectors/workfront", catch_response=True) as response:
42-
if 'Do More Work, Faster' not in response.text:
43-
response.failure("Expected test was not displayed")
42+
def about_page(self):
43+
with self.client.get("/about", catch_response=True) as response:
44+
if 'native nicaraguan' not in response.text:
45+
response.failure("Expected text was not displayed")
4446

45-
47+
# method that runs once per spawned user. Useful for setting up test data or attributes
4648
def on_start(self):
4749
print('New user was spawned')
4850

example/requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
locust_influxdb_listener == 0.0.9
1+
locust_influxdb_listener == 1.0.1
22
influxdb==5.3.1
33
locust==1.5.0

locust_influxdb_listener/__init__.py

+52-31
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Optional, Dict
2+
13
import atexit
24
import gevent
35
import logging
@@ -6,40 +8,54 @@
68
from datetime import datetime
79

810
from influxdb import InfluxDBClient
9-
from locust.exception import InterruptTaskSet
1011
from requests.exceptions import HTTPError
1112
import locust.env
13+
from urllib3 import HTTPConnectionPool
1214

13-
log = logging.getLogger('locust_influx')
15+
log = logging.getLogger('locust_influxdb_listener')
1416

1517

1618
class InfluxDBSettings:
1719
"""
18-
Store influxdb settings
20+
Store InfluxDB settings for a data connection.
1921
"""
22+
2023
def __init__(
2124
self,
22-
influx_host: str = 'localhost',
23-
influx_port: int = 8086,
25+
host: str = 'localhost',
26+
port: int = 8086,
2427
user: str = 'admin',
2528
pwd: str = 'pass',
2629
database: str = 'default',
2730
interval_ms: int = 1000,
2831
ssl: bool = False,
2932
verify_ssl: bool = False,
30-
create_database: bool = False,
31-
tags: dict = {}
33+
additional_tags: dict = {},
34+
influx_host: Optional[str] = 'localhost',
35+
influx_port: Optional[int] = 8086,
3236
):
33-
self.influx_host = influx_host
34-
self.influx_port = influx_port
37+
"""
38+
Initialize the InfluxDBSettings object with provided or default settings.
39+
40+
:param host: InfluxDB host address or hostname.
41+
:param port: InfluxDB HTTP API port.
42+
:param user: InfluxDB username for authentication.
43+
:param pwd: InfluxDB password for authentication.
44+
:param database: InfluxDB database name for storing data.
45+
:param interval_ms: Data sending interval in milliseconds.
46+
:param ssl: Enable SSL/TLS for secure data transmission.
47+
:param verify_ssl: Verify SSL certificates (only if SSL is enabled).
48+
:param additional_tags: Additional tags to include in globally for all data points.
49+
"""
50+
self.host = host if host else influx_host # Renamed from influx_host
51+
self.port = port if port else influx_port # Renamed from influx_port
3552
self.user = user
3653
self.pwd = pwd
3754
self.database = database
3855
self.interval_ms = interval_ms
3956
self.ssl = ssl
4057
self.verify_ssl = verify_ssl
41-
self.create_database = create_database
42-
self.tags = tags
58+
self.additional_tags = additional_tags
4359

4460

4561
class InfluxDBListener:
@@ -53,28 +69,35 @@ def __init__(
5369
env: locust.env.Environment,
5470
influxDbSettings: InfluxDBSettings
5571
):
72+
"""
73+
Initialize the InfluxDBListener with the provided Locust environment and InfluxDB settings.
5674
57-
# flush related attributes
75+
:param env: The Locust environment to listen for events in.
76+
:param influxDbSettings: Settings for the InfluxDB connection.
77+
"""
78+
5879
self.env = env
5980
self.cache = []
6081
self.stop_flag = False
6182
self.interval_ms = influxDbSettings.interval_ms
62-
self.tags = influxDbSettings.tags
83+
self.additional_tags = influxDbSettings.additional_tags
6384
# influxdb settings
6485
try:
86+
# try to connect create the database and switch to it
6587
self.influxdb_client = InfluxDBClient(
66-
host=influxDbSettings.influx_host,
67-
port=influxDbSettings.influx_port,
88+
host=influxDbSettings.host,
89+
port=influxDbSettings.port,
6890
username=influxDbSettings.user,
6991
password=influxDbSettings.pwd,
70-
database=influxDbSettings.database,
7192
ssl=influxDbSettings.ssl,
7293
verify_ssl=influxDbSettings.verify_ssl
7394
)
74-
if influxDbSettings.create_database:
75-
self.influxdb_client.create_database(influxDbSettings.database)
76-
except:
77-
logging.error('Could not connect to influxdb.')
95+
# database is mandatory so we should always try to create it
96+
self.influxdb_client.create_database(influxDbSettings.database)
97+
self.influxdb_client.switch_database(influxDbSettings.database)
98+
99+
except Exception as ex:
100+
logging.error(f'Unexpected error: {ex}')
78101
return
79102

80103
# determine if worker or master
@@ -85,7 +108,7 @@ def __init__(
85108
# TODO: Get real ID of slaves form locust somehow
86109
self.node_id = 'worker'
87110

88-
# start background event to push data to influx
111+
# start background event to push data to influxdb
89112
self.flush_worker = gevent.spawn(self.__flush_cached_points_worker)
90113
self.test_start(0)
91114

@@ -133,14 +156,13 @@ def __register_event(self, node_id: str, user_count: int, event: str, **_kwargs)
133156
"""
134157

135158
time = datetime.utcnow()
136-
tags = self.tags
137159
fields = {
138160
'node_id': node_id,
139161
'event': event,
140162
'user_count': user_count
141163
}
142164

143-
point = self.__make_data_point('locust_events', tags, fields, time)
165+
point = self.__make_data_point('locust_events', fields, time)
144166
self.cache.append(point)
145167

146168
def __listen_for_requests_events(self, node_id, measurement, request_type, name,
@@ -154,9 +176,8 @@ def __listen_for_requests_events(self, node_id, measurement, request_type, name,
154176

155177
time = datetime.utcnow()
156178
was_successful = True
157-
if response:
158-
# override with response code
159-
was_successful = 199 < response.status_code < 400
179+
if response is not None:
180+
was_successful = (199 < response.status_code < 400) and exception is None
160181
tags = {
161182
'node_id': node_id,
162183
'request_type': request_type,
@@ -176,7 +197,7 @@ def __listen_for_requests_events(self, node_id, measurement, request_type, name,
176197
'counter': self.env.stats.num_requests, # TODO: Review the need of this field
177198
'user_count': self.env.runner.user_count
178199
}
179-
point = self.__make_data_point(measurement, tags, fields, time)
200+
point = self.__make_data_point(measurement, fields, time, tags=tags)
180201
self.cache.append(point)
181202

182203
def __listen_for_locust_errors(self, node_id, user_instance, exception: Exception = None, tb=None) -> None:
@@ -194,7 +215,7 @@ def __listen_for_locust_errors(self, node_id, user_instance, exception: Exceptio
194215
'exception': repr(exception),
195216
'traceback': "".join(traceback.format_tb(tb)),
196217
}
197-
point = self.__make_data_point('locust_exceptions', tags, fields, time)
218+
point = self.__make_data_point('locust_exceptions', fields, time, tags=tags)
198219
self.cache.append(point)
199220

200221

@@ -210,15 +231,15 @@ def __flush_cached_points_worker(self) -> None:
210231
self.__flush_points(self.influxdb_client)
211232
gevent.sleep(self.interval_ms / 1000)
212233

213-
def __make_data_point(self, measurement: str, tags: dict, fields: dict, time: datetime) -> dict:
234+
def __make_data_point(self, measurement: str, fields: dict, time: datetime, tags: Optional[Dict[str,str]] = {}) -> dict:
214235
"""
215236
Create a list with a single point to be saved to influxdb.
216237
:param measurement: The measurement where to save this point.
217-
:param tags: Dictionary of tags to be saved in the measurement.
218238
:param fields: Dictionary of field to be saved to measurement.
219239
:param time: The time os this point.
240+
:param tags: Dictionary of tags to be saved in the measurement default to None.
220241
"""
221-
return {"measurement": measurement, "tags": {**tags, **self.tags}, "time": time, "fields": fields}
242+
return {"measurement": measurement, "tags": {**tags, **self.additional_tags}, "time": time, "fields": fields}
222243

223244

224245
def last_flush_on_quitting(self):

requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
influxdb==5.3.1
2+
locust==1.5.0

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setuptools.setup(
77
name="locust_influxdb_listener", # Replace with your own username
8-
version="0.0.9",
8+
version="1.0.1",
99
author="Pablo Calvo",
1010
author_email="[email protected]",
1111
description="Locust.io 1.X influxdb listener",

test_suite.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import unittest
2+
3+
def create_test_suite():
4+
loader = unittest.TestLoader()
5+
start_dir = "tests"
6+
suite = loader.discover(start_dir, pattern="test_*.py")
7+
return suite
8+
9+
if __name__ == "__main__":
10+
test_suite = create_test_suite()
11+
runner = unittest.TextTestRunner(verbosity=2)
12+
result = runner.run(test_suite)
13+
14+
if result.wasSuccessful():
15+
exit(0)
16+
else:
17+
exit(1)

0 commit comments

Comments
 (0)