diff --git a/CHANGELOG.md b/CHANGELOG.md index 23a6640c73..756d7547ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,65 @@ # Changelog +## [1.2.1](https://github.com/locustio/locust/tree/1.2.1) (2020-08-20) + +[Full Changelog](https://github.com/locustio/locust/compare/1.2...1.2.1) + +**Fixed bugs:** + +- ValueError: StatsEntry.use\_response\_times\_cache must be set to True [\#1531](https://github.com/locustio/locust/issues/1531) + +**Merged pull requests:** + +- fix \#1531 \(ValueError: StatsEntry.use\_response\_times\_cache must be set to True\) [\#1534](https://github.com/locustio/locust/pull/1534) ([cyberw](https://github.com/cyberw)) +- Add missing parameter to render\_template to grey out UI fields [\#1533](https://github.com/locustio/locust/pull/1533) ([max-rocket-internet](https://github.com/max-rocket-internet)) +- Update repo README with new wording, locust example, screenshots [\#1532](https://github.com/locustio/locust/pull/1532) ([max-rocket-internet](https://github.com/max-rocket-internet)) + +## [1.2](https://github.com/locustio/locust/tree/1.2) (2020-08-19) + +[Full Changelog](https://github.com/locustio/locust/compare/1.1.1...1.2) + +**Fixed bugs:** + +- Excessive precision of metrics in losust csv stats [\#1501](https://github.com/locustio/locust/issues/1501) +- WorkerRunner spawns heartbeat before setting worker\_state [\#1500](https://github.com/locustio/locust/issues/1500) +- Negative min\_response\_time shown in stats [\#1487](https://github.com/locustio/locust/issues/1487) +- Unhandled exception: ConnectionResetError, Connection reset by peer \(FastHttpUser\) [\#1472](https://github.com/locustio/locust/issues/1472) + +**Closed issues:** + +- Change the position of dividers in command line report [\#1514](https://github.com/locustio/locust/issues/1514) +- Allow negative hatch rate for ramping down [\#1488](https://github.com/locustio/locust/issues/1488) +- Missing URL to download full csv history [\#1468](https://github.com/locustio/locust/issues/1468) +- Support for completely custom load pattern / shape [\#1432](https://github.com/locustio/locust/issues/1432) +- rename "hatch rate" to "spawn rate" [\#1405](https://github.com/locustio/locust/issues/1405) + +**Merged pull requests:** + +- Doc review changes [\#1528](https://github.com/locustio/locust/pull/1528) ([phil-davis](https://github.com/phil-davis)) +- Major rework of documentation & many small fixes [\#1527](https://github.com/locustio/locust/pull/1527) ([cyberw](https://github.com/cyberw)) +- Make hatch-rate parameter deprecated instead of killing it right away. [\#1526](https://github.com/locustio/locust/pull/1526) ([cyberw](https://github.com/cyberw)) +- Move dividers \(pipe characters\) in stats command line output. Also shrink percentiles output and remove 99.999 percentile by default Fixes \#1514 [\#1525](https://github.com/locustio/locust/pull/1525) ([cyberw](https://github.com/cyberw)) +- Grey out UI input fields when LoadTestShape is in use [\#1524](https://github.com/locustio/locust/pull/1524) ([max-rocket-internet](https://github.com/max-rocket-internet)) +- Rename hatch rate to spawn rate. Fixes \#1405 [\#1523](https://github.com/locustio/locust/pull/1523) ([cyberw](https://github.com/cyberw)) +- Keep csv files open [\#1522](https://github.com/locustio/locust/pull/1522) ([lhupfeldt](https://github.com/lhupfeldt)) +- Fix issue with non str, non Exception type failure messages [\#1517](https://github.com/locustio/locust/pull/1517) ([cyberw](https://github.com/cyberw)) +- Add Feature: Download Report File [\#1516](https://github.com/locustio/locust/pull/1516) ([taojy123](https://github.com/taojy123)) +- Fix typos [\#1512](https://github.com/locustio/locust/pull/1512) ([phil-davis](https://github.com/phil-davis)) +- Fix typo of failure\_percentage in test\_stats.py [\#1511](https://github.com/locustio/locust/pull/1511) ([phil-davis](https://github.com/phil-davis)) +- Fix old HttpLocust reference in docs [\#1508](https://github.com/locustio/locust/pull/1508) ([phil-davis](https://github.com/phil-davis)) +- Adding ability to generate any custom load shape with LoadTestShape class [\#1505](https://github.com/locustio/locust/pull/1505) ([max-rocket-internet](https://github.com/max-rocket-internet)) +- Download full history - see issue 1468 [\#1504](https://github.com/locustio/locust/pull/1504) ([lhupfeldt](https://github.com/lhupfeldt)) +- Fix csv stats precision [\#1503](https://github.com/locustio/locust/pull/1503) ([vstepanov-lohika-tix](https://github.com/vstepanov-lohika-tix)) +- Allow ramping down of users [\#1502](https://github.com/locustio/locust/pull/1502) ([max-rocket-internet](https://github.com/max-rocket-internet)) +- Add 2 things to .gitignore [\#1498](https://github.com/locustio/locust/pull/1498) ([max-rocket-internet](https://github.com/max-rocket-internet)) +- Print valid URL when --web-host is not specified [\#1496](https://github.com/locustio/locust/pull/1496) ([dmitrytokarev](https://github.com/dmitrytokarev)) +- Replace time.time\(\) with time.monotonic\(\) [\#1492](https://github.com/locustio/locust/pull/1492) ([max-rocket-internet](https://github.com/max-rocket-internet)) +- Remove "Loadgen" from CPU warning log messages [\#1491](https://github.com/locustio/locust/pull/1491) ([max-rocket-internet](https://github.com/max-rocket-internet)) +- Fix small typo in docker docs [\#1490](https://github.com/locustio/locust/pull/1490) ([max-rocket-internet](https://github.com/max-rocket-internet)) +- fade into the running screen before getting a response from the server [\#1479](https://github.com/locustio/locust/pull/1479) ([camilojimenez](https://github.com/camilojimenez)) +- Refactoring stats to handle custom percentiles [\#1477](https://github.com/locustio/locust/pull/1477) ([vstepanov-lohika-tix](https://github.com/vstepanov-lohika-tix)) +- Handle connection reset error in fast http client [\#1475](https://github.com/locustio/locust/pull/1475) ([mkarlovich](https://github.com/mkarlovich)) + ## [1.1.1](https://github.com/locustio/locust/tree/1.1.1) (2020-07-07) [Full Changelog](https://github.com/locustio/locust/compare/1.1...1.1.1) @@ -475,6 +535,7 @@ - Allow None response time for requests [\#1088](https://github.com/locustio/locust/pull/1088) ([cyberw](https://github.com/cyberw)) - Fixed issue with Total Requests Per Second plot [\#1060](https://github.com/locustio/locust/pull/1060) ([williamlhunter](https://github.com/williamlhunter)) - Tox: Add flake8 tests to find Python syntax errors and undefined names [\#1039](https://github.com/locustio/locust/pull/1039) ([cclauss](https://github.com/cclauss)) +- Fix frontend bugs. [\#822](https://github.com/locustio/locust/pull/822) ([omittones](https://github.com/omittones)) - Switch from using optparse to argparse for command line arguments [\#769](https://github.com/locustio/locust/pull/769) ([jdufresne](https://github.com/jdufresne)) - Allow skipping the logging setup [\#738](https://github.com/locustio/locust/pull/738) ([Exide](https://github.com/Exide)) - Added link to an Ansible role as a 3rd party tool. [\#704](https://github.com/locustio/locust/pull/704) ([tinx](https://github.com/tinx)) @@ -812,6 +873,7 @@ - \(libev\) select: Invalid argument when trying to go past 1k users [\#121](https://github.com/locustio/locust/issues/121) - Command line option to specify the duration to run [\#71](https://github.com/locustio/locust/issues/71) - Setup/teardown hooks [\#59](https://github.com/locustio/locust/issues/59) +- Define wait times by function instead of variable [\#18](https://github.com/locustio/locust/issues/18) **Merged pull requests:** @@ -825,7 +887,6 @@ - Change name of msgpack dependency. [\#841](https://github.com/locustio/locust/pull/841) ([vamega](https://github.com/vamega)) - response time doesn't need to be cast to int, as this is implicit in … [\#830](https://github.com/locustio/locust/pull/830) ([efology](https://github.com/efology)) - Add tasks sequence support [\#827](https://github.com/locustio/locust/pull/827) ([Ramshell](https://github.com/Ramshell)) -- Fix frontend bugs. [\#822](https://github.com/locustio/locust/pull/822) ([omittones](https://github.com/omittones)) - Fix some typos in events.py [\#820](https://github.com/locustio/locust/pull/820) ([felixonmars](https://github.com/felixonmars)) - Update all pypi.python.org URLs to pypi.org [\#818](https://github.com/locustio/locust/pull/818) ([jdufresne](https://github.com/jdufresne)) - Update third-party-tools.rst [\#808](https://github.com/locustio/locust/pull/808) ([anhldbk](https://github.com/anhldbk)) @@ -874,7 +935,6 @@ - make test is failing on 0.7 tags due to Flask 0.12 [\#637](https://github.com/locustio/locust/issues/637) - num-requests bug [\#512](https://github.com/locustio/locust/issues/512) - Run the tests for the specified time [\#196](https://github.com/locustio/locust/issues/196) -- loglevel and logfile don't seem to work [\#25](https://github.com/locustio/locust/issues/25) - Remove support for plain sockets for master/slave communication [\#14](https://github.com/locustio/locust/issues/14) ## [v0.8](https://github.com/locustio/locust/tree/v0.8) (2017-09-19) @@ -1015,7 +1075,6 @@ - Suggest Python version [\#231](https://github.com/locustio/locust/issues/231) - Changing locustfile.py on master via UI and having master / slave replication [\#209](https://github.com/locustio/locust/issues/209) - Option to prevent stats from being reset when all locusts are hatched [\#205](https://github.com/locustio/locust/issues/205) -- PUT requests are shown as GET [\#204](https://github.com/locustio/locust/issues/204) - Cannot simulate one single user [\#178](https://github.com/locustio/locust/issues/178) - Feature request: Stepped hatch rate [\#168](https://github.com/locustio/locust/issues/168) - Having a locust "die" or stop after one task [\#161](https://github.com/locustio/locust/issues/161) @@ -1243,6 +1302,7 @@ - multiple slaves of different server specs ? [\#210](https://github.com/locustio/locust/issues/210) - multiple url tests ? [\#208](https://github.com/locustio/locust/issues/208) - how to get the recorded data [\#206](https://github.com/locustio/locust/issues/206) +- PUT requests are shown as GET [\#204](https://github.com/locustio/locust/issues/204) - http proxy support [\#203](https://github.com/locustio/locust/issues/203) - error report is not included in `--logfile` nor is it available for download as a csv [\#202](https://github.com/locustio/locust/issues/202) - Stats get corrupted when the number of swarm users reaches the objective [\#201](https://github.com/locustio/locust/issues/201) @@ -1447,13 +1507,16 @@ [Full Changelog](https://github.com/locustio/locust/compare/v0.5...v0.5.1) +**Closed issues:** + +- loglevel and logfile don't seem to work [\#25](https://github.com/locustio/locust/issues/25) + ## [v0.5](https://github.com/locustio/locust/tree/v0.5) (2012-07-01) [Full Changelog](https://github.com/locustio/locust/compare/v0.4...v0.5) **Closed issues:** -- Define wait times by function instead of variable [\#18](https://github.com/locustio/locust/issues/18) - Remove Confluence specific task ratio formatter [\#13](https://github.com/locustio/locust/issues/13) - Add HTTP request method \(GET/POST/PUT, etc\) to statistics table [\#3](https://github.com/locustio/locust/issues/3) diff --git a/README.md b/README.md index fa986ca23d..9216eedcc9 100644 --- a/README.md +++ b/README.md @@ -7,73 +7,68 @@ [![PyPI](https://img.shields.io/pypi/pyversions/locust.svg)](https://pypi.org/project/locust/) [![GitHub contributors](https://img.shields.io/github/contributors/locustio/locust.svg)](https://github.com/locustio/locust/graphs/contributors) -## Links +Locust is an easy to use, scriptable and scalable performance testing tool. You define the behaviour of your users in regular Python code, instead of using a clunky UI or domain specific language. This makes Locust infinitely expandable and very developer friendly. -* Website: locust.io -* Documentation: docs.locust.io -* Code/issues: Github -* Support/Questions: StackOverflow -* Chat/discussion: [Slack signup](https://slack.locust.io/) +## Features -## Description +#### Write user test scenarios in plain-old Python -Locust is an easy-to-use, distributed, user load testing tool. It is intended for load-testing web sites (or other systems) and -figuring out how many concurrent users a system can handle. +If you want your users to loop, perform some conditional behaviour or do some calculations, you just use the regular programming constructs provided by Python. Locust runs every user inside its own greenlet (a lightweight process/coroutine). This enables you to write your tests like normal (blocking) Python code instead of having to use callbacks or some other mechanism. Because your scenarios are “just python” you can use your regular IDE, and version control your tests as regular code (as opposed to some other tools that use XML or binary formats) -The idea is that during a test, a swarm of simulated users will attack your website. The behavior of each user is defined by you -using Python code, and the swarming process is monitored from a web UI in real-time. This will help you battle test and identify -bottlenecks in your code before letting real users in. +```python +from locust import HttpUser, task, between -Locust is completely event-based, and therefore it's possible to support thousands of concurrent users on a single machine. -In contrast to many other event-based apps it doesn't use callbacks. Instead it uses light-weight processes, through gevent. -Each locust swarming your site is actually running inside its own process (or greenlet, to be correct). -This allows you to write very expressive scenarios in Python without complicating your code with callbacks. +class QuickstartUser(HttpUser): + wait_time = between(1, 2) + def on_start(self): + self.client.post("/login", json={"username":"foo", "password":"bar"}) -## Features -* **Write user test scenarios in plain-old Python**
- No need for clunky UIs or bloated XML—just code as you normally would. Based on coroutines instead -of callbacks, your code looks and behaves like normal, blocking Python code. + @task + def index_page(self): + self.client.get("/hello") + self.client.get("/world") + + @task(3) + def view_item(self): + for item_id in range(10): + self.client.get(f"/item?id={item_id}", name="/item") +``` -* **Distributed & Scalable - supports hundreds of thousands of users**
- Locust supports running load tests distributed over multiple machines. - Being event-based, even one Locust node can handle thousands of users in a single process. - Part of the reason behind this is that even if you simulate that many users, not all are actively hitting your system. Often, users are idle figuring out what to do next. Requests per second != number of users online. +#### Distributed & Scalable - supports hundreds of thousands of users -* **Web-based UI**
- Locust has a neat HTML+JS that shows all relevant test details in real-time. And since the UI is web-based, it's cross-platform and easily extendable. +Locust makes it easy to run load tests distributed over multiple machines. It is event-based (using [gevent](http://www.gevent.org/)), which makes it possible for a single process to handle many thousands concurrent users. While there may be other tools that are capable of doing more requests per second on a given hardware, the low overhead of each Locust user makes it very suitable for testing highly concurrent workloads. -* **Can test any system**
- Even though Locust is web-oriented, it can be used to test almost any system. Just write a client for what ever you wish to test and swarm it with users! It's super easy! +#### Web-based UI -* **Hackable**
- Locust is very small and very hackable and we intend to keep it that way. All heavy-lifting of evented I/O and coroutines are delegated to gevent. The brittleness of alternative testing tools was the reason we created Locust. +Locust has a user friendly web interface that shows the progress of your test in real-time. You can even change the load while the test is running. It can also be run without the UI, making it easy to use for CI/CD testing. +Locust UI charts Locust UI stats Locust UI workers Locust UI start test -## Documentation +#### Can test any system -More info and documentation can be found at: https://docs.locust.io/ +Even though Locust primarily works with web sites/services, it can be used to test almost any system or protocol. Just [write a client](https://docs.locust.io/en/latest/testing-other-systems.html#testing-other-systems) for what you want to test, or [explore some created by the community](https://github.com/SvenskaSpel/locust-plugins#users). -## Questions/help? +## Hackable -For questions about how to use Locust, feel free to stop by the Slack or ask questions on Stack Overflow tagged Locust. +Locust is small and very flexible and we intend to keep it that way. If you want to [send reporting data to that database & graphing system you like](https://github.com/SvenskaSpel/locust-plugins/blob/master/locust_plugins/listeners.py), wrap calls to a REST API to handle the particulars of your system or run a [totally custom load pattern](https://docs.locust.io/en/latest/generating-custom-load-shape.html#generating-custom-load-shape), there is nothing stopping you! -## Bug reporting +## Links -Open a Github issue and follow the template listed there. +* Website: [locust.io](https://locust.io) +* Documentation: [docs.locust.io](https://docs.locust.io) +* Code/issues: [Github](https://github.com/locustio/locust) +* Support/Questions: [StackOverflow](https://stackoverflow.com/questions/tagged/locust) +* Chat/discussion: [Slack signup](https://slack.locust.io/) ## Authors -- Carl Byström (@cgbystrom on Twitter) -- Jonatan Heyman (@jonatanheyman on Twitter) -- Joakim Hamrén (@Jahaaja) -- Hugo Heyman (@hugoheyman) +* [Carl Bystr](http://cgbystrom.com) ([@cgbystrom](https://twitter.com/cgbystrom) on Twitter) +* [Jonatan Heyman](http://heyman.info) ([@jonatanheyman](https://twitter.com/jonatanheyman) on Twitter) +* [Joakim Hamrén](https://github.com/Jahaja) ([@Jahaaja](https://twitter.com/Jahaaja) on Twitter) +* [Hugo Heyman](https://github.com/HeyHugo) ([@hugoheyman](https://twitter.com/hugoheyman) on Twitter) +* [Lars Holmberg](https://github.com/cyberw) ## License Open source licensed under the MIT license (see _LICENSE_ file for details). - - -## Supported Python Versions - -Locust is supported on Python 3.6, 3.7, 3.8. diff --git a/docs/api.rst b/docs/api.rst index 6c8d56bdc8..22f616fcc6 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -73,11 +73,20 @@ ResponseContextManager class :members: success, failure -InterruptTaskSet Exception -========================== +.. _exceptions: + +Exceptions +========== + .. autoexception:: locust.exception.InterruptTaskSet +.. autoexception:: locust.exception.RescheduleTask + + +.. autoexception:: locust.exception.RescheduleTaskImmediately + + Environment class ================= .. autoclass:: locust.env.Environment @@ -89,7 +98,7 @@ Environment class Event hooks =========== -Locust provide event hooks that can be used to extend Locus in various ways. +Locust provides event hooks that can be used to extend Locust in various ways. The following event hooks are available under :py:attr:`Environment.events `, and there's also a reference to these events under ``locust.events`` that can be used at the module level diff --git a/docs/changelog.rst b/docs/changelog.rst index 60d6aec1b6..a46854b375 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,15 +4,21 @@ Changelog Highlights For full details of the Locust changelog, please see https://github.com/locustio/locust/blob/master/CHANGELOG.md +1.2.1 +===== + +* Bug fix (StatsEntry.use_response_times_cache must be set to True, https://github.com/locustio/locust/issues/1531) + 1.2 === -* Rename hatch rate to spawn rate (this may be a breaking change in some cases!) +* Rename hatch rate to spawn rate (the --hatch-rate parameter is only deprecated, but the hatch_complete event has been renamed spawning_complete) * Ability to generate any custom load shape with LoadTestShape class * Allow ramping down of users * Ability to use save custom percentiles * Improve command line stats output * Bug fixes (excessive precision of metrics in losust csv stats, negative response time when system clock has changed, issue with non-string failure messages, some typos etc) +* Documentation improvements 1.1.1 ===== diff --git a/docs/extending-locust.rst b/docs/extending-locust.rst index 4a75ddbd42..b02e07499b 100644 --- a/docs/extending-locust.rst +++ b/docs/extending-locust.rst @@ -1,8 +1,8 @@ -================= -Extending Locust -================= +================================== +Extending Locust using event hooks +================================== -Locust comes with a number of events hooks that can be used to extend Locust in different ways. +Locust comes with a number of event hooks that can be used to extend Locust in different ways. Event hooks live on the Environment instance under the :py:attr:`events ` attribute. However, since the Environment instance hasn't been created when locustfiles are imported, @@ -26,7 +26,7 @@ Here's an example on how to set up an event listener:: .. seealso:: - To see all available event, please see :ref:`events`. + To see all available events, please see :ref:`events`. @@ -74,3 +74,9 @@ For example, you can monitor the fail ratio of your test and stop the run if it # only run this on master & standalone if not isinstance(environment.runner, WorkerRunner): gevent.spawn(checker, environment) + + +More examples +============= + +See `locust-plugins `_ diff --git a/docs/generating-custom-load-shape.rst b/docs/generating-custom-load-shape.rst index b53134e567..bb49eddd6b 100644 --- a/docs/generating-custom-load-shape.rst +++ b/docs/generating-custom-load-shape.rst @@ -1,38 +1,39 @@ .. _generating-custom-load-shape: -================================= -Generating a custom load shape using a `LoadTestShape` class -================================= +============================== +Generating a custom load shape +============================== -Sometimes a completely custom shaped load test is required that cannot be achieved by simply setting or changing the user count and spawn rate. For example, generating a spike during a test or ramping up and down at custom times. In these cases using the `LoadTestShape` class can give complete control over the user count and spawn rate at all times. +Sometimes a completely custom shaped load test is required that cannot be achieved by simply setting or changing the user count and spawn rate. For example, you might want to generate a load spike or ramp up and down at custom times. By using a `LoadTestShape` class you have full control over the user count and spawn rate at all times. -How does a `LoadTestShape` class work? ---------------------------------------------- +Define a class inheriting the LoadTestShape class in your locust file. If this type of class is found then it will be automatically used by Locust. -Define your class inheriting the `LoadTestShape` class in your locust file. If this type of class is found then it will be automatically used by Locust. Add a `tick()` method to return either a tuple containing the user count and spawn rate or `None` to stop the load test. Locust will call the `tick()` method every second and update the load test with new settings or stop the test. +In this class you define a `tick()` method that returns a tuple with the desired user count and spawn rate (or `None` to stop the test). Locust will call the `tick()` method approximately once per second. -Examples ---------------------------------------------- +In the class you also have access to the `get_run_time()` method, for checking how long the test has run for. -There are also some [examples on github](https://github.com/locustio/locust/tree/master/examples/custom_shape) including: +Example +------- -- Generating a double wave shape -- Time based stages like K6 -- Step load pattern like Visual Studio +This shape class will increase user count in blocks of 100 and then stop the load test after 10 minutes: + +.. code-block:: python -Here is a simple example that will increase user count in blocks of 100 then stop the load test after 10 minutes: + class MyCustomShape(LoadTestShape): + time_limit = 600 + spawn_rate = 20 + + def tick(self): + run_time = self.get_run_time() -```python -class MyCustomShape(LoadTestShape): - time_limit = 600 - spawn_rate = 20 - - def tick(self): - run_time = self.get_run_time() + if run_time < self.time_limit: + user_count = round(run_time, -2) + return (user_count, spawn_rate) - if run_time < self.time_limit: - user_count = round(run_time, -2) - return (user_count, spawn_rate) + return None - return None -``` +There are also some `examples on github `_ including: + +- Generating a double wave shape +- Time based stages like K6 +- Step load pattern like Visual Studio diff --git a/docs/history.rst b/docs/history.rst new file mode 100644 index 0000000000..f75705f488 --- /dev/null +++ b/docs/history.rst @@ -0,0 +1,28 @@ +.. _history: + +=============================== +The history of Locust +=============================== + +Locust was created because we were fed up with existing solutions. None of them are solving the +right problem and to me, they are missing the point. We've tried both Apache JMeter and Tsung. +Both tools are quite OK to use; we've used the former many times benchmarking stuff at work. +JMeter comes with a UI, which you might think for a second is a good thing. But you soon realize it's +a PITA to "code" your testing scenarios through some point-and-click interface. Secondly, JMeter +is thread-bound. This means for every user you want to simulate, you need a separate thread. +Needless to say, benchmarking thousands of users on a single machine just isn't feasible. + +Tsung, on the other hand, does not have these thread issues as it's written in Erlang. It can make +use of the light-weight processes offered by BEAM itself and happily scale up. But when it comes to +defining the test scenarios, Tsung is as limited as JMeter. It offers an XML-based DSL to define how +a user should behave when testing. I guess you can imagine the horror of "coding" this. Displaying +any sorts of graphs or reports when completed requires you to post-process the log files generated from +the test. Only then can you get an understanding of how the test went. + +Anyway, we've tried to address these issues when creating Locust. Hopefully none of the above +pain points should exist. + +I guess you could say we're really just trying to scratch our own itch here. We hope others will +find it as useful as we do. + +- `Jonatan Heyman `_ (`@jonatanheyman `_ on Twitter) diff --git a/docs/index.rst b/docs/index.rst index be36afeaa6..164534591c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -2,8 +2,6 @@ Locust Documentation ===================== -.. rubric:: Everything you need to know about Locust - .. sidebar:: About locust Locust is a scalable load testing framework written in Python diff --git a/docs/installation.rst b/docs/installation.rst index 58825d5ae6..63c502b809 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,3 +1,5 @@ +.. _installation: + Installation ============ diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 4387785270..cb65d0c126 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -66,7 +66,7 @@ Methods declared with the ``@task`` attribute are the core of your locust file. self.client.get("/hello") self.client.get("/world") -The self.client attribute makes it possible to make HTTP calls that will be logged by Locust. `See more details on how to make requests, validate the response, etc `_. +The self.client attribute makes it possible to make HTTP calls that will be logged by Locust. For information on how to make other kinds of requests, validate the response, etc, see `Using the HTTP Client `_. .. code-block:: python @@ -142,6 +142,6 @@ Parameters can also be set through :ref:`environment variables `. How to write a *real* locust file? -================================== +"""""""""""""""""""""""""""""""""" The above example was just the bare minimum, see :ref:`writing-a-locustfile` for more info. diff --git a/docs/running-locust-distributed.rst b/docs/running-locust-distributed.rst index 6a3514a3eb..3886e34f00 100644 --- a/docs/running-locust-distributed.rst +++ b/docs/running-locust-distributed.rst @@ -27,7 +27,7 @@ processor core** on the worker machines. Otherwise - due to the current implementation - you might end up with a distribution of the User classes that doesn't correspond to the User classes' ``weight`` attribute. And if the spawn rate is lower than the number of worker - nodes, the spawning would occur in "bursts" where all worker node would spawn a single user and + nodes, the spawning would occur in "bursts" where all worker nodes would spawn a single user and then sleep for multiple seconds, spawn another user, sleep and repeat. @@ -38,7 +38,7 @@ To start locust in master mode:: locust -f my_locustfile.py --master -And then on each worker (replace ``192.168.0.14`` with IP of the master machine, or leave out the parameter altogether if your workers are on the same machine as the master):: +And then on each worker (replace ``192.168.0.14`` with the IP of the master machine, or leave out the parameter altogether if your workers are on the same machine as the master):: locust -f my_locustfile.py --worker --master-host=192.168.0.14 @@ -101,7 +101,7 @@ See :ref:`running-locust-distributed-without-web-ui` Generating a custom load shape using a `LoadTestShape` class -============================================= +============================================================ See :ref:`generating-custom-load-shape` diff --git a/docs/running-locust-in-step-load-mode.rst b/docs/running-locust-in-step-load-mode.rst index 86b3abbb1d..17e55b8c6c 100644 --- a/docs/running-locust-in-step-load-mode.rst +++ b/docs/running-locust-in-step-load-mode.rst @@ -48,5 +48,5 @@ Running Locust distributed in step load mode --------------------------------------------- If you want to :ref:`run Locust distributed ` in step load mode, -you should specify the ``--step-load`` option when starting the master node, to swarm locusts by step. It will then show the ``--step-users`` option and ``--step-time`` option in Locust UI. +you should specify the ``--step-load`` option when starting the master node, to swarm locusts by step. It will then show the ``--step-users`` option and ``--step-time`` option in the Locust UI. diff --git a/docs/testing-other-systems.rst b/docs/testing-other-systems.rst index c98ac930a1..f1142856c4 100644 --- a/docs/testing-other-systems.rst +++ b/docs/testing-other-systems.rst @@ -1,3 +1,5 @@ +.. _testing-other-systems: + =========================================== Testing other systems using custom clients =========================================== @@ -18,12 +20,12 @@ Here is an example of a User class, **XmlRpcUser**, which provides an XML-RPC cl If you've written Locust tests before, you'll recognize the class called ``ApiUser`` which is a normal User class that has a couple of tasks declared. However, the ``ApiUser`` inherits from ``XmlRpcUser`` that you can see right above ``ApiUser``. The ``XmlRpcUser`` is marked as abstract -using ``abstract = True`` which means that Locust till not try to create simulated users from that class -(only of classes that extends it). ``XmlRpcUser`` provides an instance of XmlRpcClient under the +using ``abstract = True`` which means that Locust will not try to create simulated users from that class +(only of classes that extend it). ``XmlRpcUser`` provides an instance of XmlRpcClient under the ``client`` attribute. The ``XmlRpcClient`` is a wrapper around the standard -library's :py:class:`xmlrpc.client.ServerProxy`. It basically just proxies the function calls, but with the +library's :py:class:`xmlrpc.client.ServerProxy`. It basically just proxies the function calls, but with the important addition of firing :py:attr:`locust.event.Events.request_success` and :py:attr:`locust.event.Events.request_failure` events, which will record all calls in Locust's statistics. @@ -31,3 +33,4 @@ Here's an implementation of an XML-RPC server that would work as a server for th .. literalinclude:: ../examples/custom_xmlrpc_client/server.py +For more examples, see `locust-plugins `_ diff --git a/docs/what-is-locust.rst b/docs/what-is-locust.rst index b8c1691140..40ed0c0e46 100644 --- a/docs/what-is-locust.rst +++ b/docs/what-is-locust.rst @@ -2,85 +2,63 @@ What is Locust? =============================== -Locust is an easy-to-use, distributed, user load testing tool. It is intended for load-testing web sites -(or other systems) and figuring out how many concurrent users a system can handle. +Locust is an easy to use, scriptable and scalable performance testing tool. -The idea is that during a test, a swarm of `locust `_ users -will attack your website. The behavior of each user is defined by you using Python code, and the -swarming process is monitored from a web UI in real-time. This will help you battle test and identify -bottlenecks in your code before letting real users in. +You define the behaviour of your users in regular Python code, instead of using a clunky UI or domain specific language. -Locust is completely event-based, and therefore it's possible to support thousands of concurrent -users on a single machine. In contrast to many other event-based apps it doesn't use callbacks. -Instead it uses light-weight processes, through `gevent `_. Each locust -swarming your site is actually running inside its own process (or greenlet, to be correct). This -allows you to write very expressive scenarios in Python without complicating your code with callbacks. +This makes Locust infinitely expandable and very developer friendly. +To start using Locust, go to :ref:`installation` Features ======== * **Write user test scenarios in plain-old Python** - No need for clunky UIs or bloated XML—just code as you normally would. Based on coroutines instead - of callbacks, your code looks and behaves like normal, blocking Python code. + If you want your users to loop, perform some conditional behaviour or do some calculations, you just use the regular programming constructs provided by Python. + Locust runs every user inside its own greenlet (a lightweight process/coroutine). This enables you to write your tests like normal (blocking) Python code instead of having to use callbacks or some other mechanism. + Because your scenarios are "just python" you can use your regular IDE, and version control your tests as regular code (as opposed to some other tools that use XML or binary formats) * **Distributed & Scalable - supports hundreds of thousands of users** - Locust supports running load tests distributed over multiple machines. - Being event-based, even one Locust node can handle thousands of users in a single process. - Part of the reason behind this is that even if you simulate that many users, not all are actively - hitting your system. Often, users are idle figuring out what to do next. - Requests per second != number of users online. - + Locust makes it easy to run load tests distributed over multiple machines. + It is event-based (using `gevent `_), which makes it possible for a single process to handle many thousands concurrent users. + While there may be other tools that are capable of doing more requests per second on a given hardware, the low overhead of each Locust user makes it very suitable for testing highly concurrent workloads. + * **Web-based UI** - Locust has a neat HTML+JS user interface that shows relevant test details in real-time. And since - the UI is web-based, it's cross-platform and easily extendable. + Locust has a user friendly web interface that shows the progress of your test in real-time. You can even change the load while the test is running. It can also be run without the UI, making it easy to use for CI/CD testing. * **Can test any system** - Even though Locust is web-oriented, it can be used to test almost any system. Just write a client - for what ever you wish to test and swarm it with locusts! It's super easy! + Even though Locust primarily works with web sites/services, it can be used to test almost any system or protocol. Just :ref:`write a client ` + for what you want to test, or `explore some created by the community `_. * **Hackable** - Locust is small and very hackable and we intend to keep it that way. All heavy-lifting of evented - I/O and coroutines are delegated to gevent. The brittleness of alternative testing tools was the - reason we created Locust. - -Background -========== + Locust is small and very flexible and we intend to keep it that way. If you want to `send reporting data to that database & graphing system you like `_, wrap calls to a REST API to handle the particulars of your system or run a :ref:`totally custom load pattern `, there is nothing stopping you! -Locust was created because we were fed up with existing solutions. None of them are solving the -right problem and to me, they are missing the point. We've tried both Apache JMeter and Tsung. -Both tools are quite OK to use; we've used the former many times benchmarking stuff at work. -JMeter comes with a UI, which you might think for a second is a good thing. But you soon realize it's -a PITA to "code" your testing scenarios through some point-and-click interface. Secondly, JMeter -is thread-bound. This means for every user you want to simulate, you need a separate thread. -Needless to say, benchmarking thousands of users on a single machine just isn't feasible. +Name & background +================= -Tsung, on the other hand, does not have these thread issues as it's written in Erlang. It can make -use of the light-weight processes offered by BEAM itself and happily scale up. But when it comes to -defining the test scenarios, Tsung is as limited as JMeter. It offers an XML-based DSL to define how -a user should behave when testing. I guess you can imagine the horror of "coding" this. Displaying -any sorts of graphs or reports when completed requires you to post-process the log files generated from -the test. Only then can you get an understanding of how the test went. +`Locust `_ takes its name from the grasshopper species, known for their swarming behaviour. -Anyway, we've tried to address these issues when creating Locust. Hopefully none of the above -pain points should exist. +Previous versions of Locust used terminology borrowed from nature (swarming, hatching, attacking etc), but now employs more industry standard naming. -I guess you could say we're really just trying to scratch our own itch here. We hope others will -find it as useful as we do. +:ref:`history` Authors ======= - `Jonatan Heyman `_ (`@jonatanheyman `_ on Twitter) +- Lars Holmberg (`@cyberw `_ on Github) - Carl Byström (`@cgbystrom `_ on Twitter) - Joakim Hamrén (`@Jahaaja `_ on Twitter) - Hugo Heyman (`@hugoheyman `_ on Twitter) +Many thanks to our other great `contributors `_! + + License ======= diff --git a/docs/writing-a-locustfile.rst b/docs/writing-a-locustfile.rst index e235ec584f..8540a398d6 100644 --- a/docs/writing-a-locustfile.rst +++ b/docs/writing-a-locustfile.rst @@ -118,7 +118,7 @@ Tasks When a load test is started, an instance of a User class will be created for each simulated user and they will start running within their own green thread. When these users run they pick tasks that -they execute, sleeps for awhile, and then picks a new task and so on. +they execute, sleep for awhile, and then pick a new task and so on. The tasks are normal python callables and - if we were load-testing an auction website - they could do stuff like "loading the start page", "searching for some product", "making a bid", etc. @@ -192,7 +192,7 @@ Here is an example of a User task declared as a normal python function: If the tasks attribute is specified as a list, each time a task is to be performed, it will be randomly chosen from the *tasks* attribute. If however, *tasks* is a dict - with callables as keys and ints as values - the task that is to be executed will be chosen at random but with the int as ratio. So -with a tasks that looks like this:: +with a task that looks like this:: {my_task: 3, another_task: 1} @@ -565,7 +565,7 @@ You can mark a request as failed by using the *catch_response* argument, a *with a call to *response.failure()* .. code-block:: python - + with self.client.get("/", catch_response=True) as response: if response.text != "Success": response.failure("Got wrong response") @@ -581,6 +581,16 @@ You can also mark a request as successful, even if the response code was bad: if response.status_code == 404: response.success() +You can even avoid logging a request at all by throwing an exception and then catching it outside the with-block. Or you can throw a :ref:`locust exception `, like in the example below, and let locust catch it. + +.. code-block:: python + + from locust.exception import RescheduleTask + ... + with self.client.get("/does_not_exist/", catch_response=True) as response: + if response.status_code == 404: + raise RescheduleTask() + .. _name-parameter: diff --git a/locust/__init__.py b/locust/__init__.py index d6ec26e30d..a116b381cd 100644 --- a/locust/__init__.py +++ b/locust/__init__.py @@ -13,7 +13,7 @@ from .event import Events events = Events() -__version__ = "1.1.1" +__version__ = "1.2.1" __all__ = ( "SequentialTaskSet", "wait_time", diff --git a/locust/argument_parser.py b/locust/argument_parser.py index 5ab54ba876..2a686504b1 100644 --- a/locust/argument_parser.py +++ b/locust/argument_parser.py @@ -146,7 +146,8 @@ def setup_parser_arguments(parser): parser.add_argument( '--hatch-rate', env_var="LOCUST_HATCH_RATE", - action='store_true', + type=float, + default=0, help=configargparse.SUPPRESS, ) parser.add_argument( diff --git a/locust/exception.py b/locust/exception.py index a084d194cd..c9490f74af 100644 --- a/locust/exception.py +++ b/locust/exception.py @@ -29,13 +29,13 @@ class RescheduleTask(Exception): """ When raised in a task it's equivalent of a return statement. - Used internally by TaskSet. When raised within the task control flow of a TaskSet, + Also used internally by TaskSet. When raised within the task control flow of a TaskSet, but not inside a task, the execution should be handed over to the parent TaskSet. """ class RescheduleTaskImmediately(Exception): """ - When raised in a User task, another User task will be rescheduled immediately + When raised in a User task, another User task will be rescheduled immediately (without calling wait_time first) """ class RPCError(Exception): diff --git a/locust/main.py b/locust/main.py index 29e2d6e469..e1028941d2 100644 --- a/locust/main.py +++ b/locust/main.py @@ -142,8 +142,8 @@ def main(): sys.exit(1) if options.hatch_rate: - sys.stderr.write("The --hatch-rate parameter has been renamed --spawn-rate\n") - sys.exit(1) + sys.stderr.write("[DEPRECATED] The --hatch-rate parameter has been renamed --spawn-rate\n") + options.spawn_rate = options.hatch_rate # setup logging diff --git a/locust/static/img/ui-screenshot-charts.png b/locust/static/img/ui-screenshot-charts.png new file mode 100644 index 0000000000..74ad11e3cf Binary files /dev/null and b/locust/static/img/ui-screenshot-charts.png differ diff --git a/locust/static/img/ui-screenshot-start-test.png b/locust/static/img/ui-screenshot-start-test.png new file mode 100644 index 0000000000..04a41887df Binary files /dev/null and b/locust/static/img/ui-screenshot-start-test.png differ diff --git a/locust/static/img/ui-screenshot-stats.png b/locust/static/img/ui-screenshot-stats.png new file mode 100644 index 0000000000..f1d8652a39 Binary files /dev/null and b/locust/static/img/ui-screenshot-stats.png differ diff --git a/locust/static/img/ui-screenshot-workers.png b/locust/static/img/ui-screenshot-workers.png new file mode 100644 index 0000000000..2b73485096 Binary files /dev/null and b/locust/static/img/ui-screenshot-workers.png differ diff --git a/locust/web.py b/locust/web.py index 14fe370994..d76723e950 100644 --- a/locust/web.py +++ b/locust/web.py @@ -142,6 +142,7 @@ def index(): step_time=options and options.step_time, worker_count=worker_count, is_step_load=environment.step_load, + is_shape=environment.shape_class, stats_history_enabled=options and options.stats_history_enabled, )