locust 2.18.1.dev10__py3-none-any.whl → 2.18.1.dev16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of locust might be problematic. Click here for more details.

locust/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.18.1.dev10'
16
- __version_tuple__ = version_tuple = (2, 18, 1, 'dev10')
15
+ __version__ = version = '2.18.1.dev16'
16
+ __version_tuple__ = version_tuple = (2, 18, 1, 'dev16')
locust/log.py CHANGED
@@ -9,6 +9,15 @@ HOSTNAME = socket.gethostname()
9
9
  unhandled_greenlet_exception = False
10
10
 
11
11
 
12
+ class LogReader(logging.Handler):
13
+ def __init__(self):
14
+ super().__init__()
15
+ self.logs = []
16
+
17
+ def emit(self, record):
18
+ self.logs.append(self.format(record))
19
+
20
+
12
21
  def setup_logging(loglevel, logfile=None):
13
22
  loglevel = loglevel.upper()
14
23
 
@@ -32,21 +41,22 @@ def setup_logging(loglevel, logfile=None):
32
41
  "class": "logging.StreamHandler",
33
42
  "formatter": "plain",
34
43
  },
44
+ "log_reader": {"class": "locust.log.LogReader", "formatter": "default"},
35
45
  },
36
46
  "loggers": {
37
47
  "locust": {
38
- "handlers": ["console"],
48
+ "handlers": ["console", "log_reader"],
39
49
  "level": loglevel,
40
50
  "propagate": False,
41
51
  },
42
52
  "locust.stats_logger": {
43
- "handlers": ["console_plain"],
53
+ "handlers": ["console_plain", "log_reader"],
44
54
  "level": "INFO",
45
55
  "propagate": False,
46
56
  },
47
57
  },
48
58
  "root": {
49
- "handlers": ["console"],
59
+ "handlers": ["console", "log_reader"],
50
60
  "level": loglevel,
51
61
  },
52
62
  }
@@ -58,8 +68,8 @@ def setup_logging(loglevel, logfile=None):
58
68
  "filename": logfile,
59
69
  "formatter": "default",
60
70
  }
61
- LOGGING_CONFIG["loggers"]["locust"]["handlers"] = ["file"]
62
- LOGGING_CONFIG["root"]["handlers"] = ["file"]
71
+ LOGGING_CONFIG["loggers"]["locust"]["handlers"] = ["file", "log_reader"]
72
+ LOGGING_CONFIG["root"]["handlers"] = ["file", "log_reader"]
63
73
 
64
74
  logging.config.dictConfig(LOGGING_CONFIG)
65
75
 
locust/test/test_web.py CHANGED
@@ -5,13 +5,13 @@ import os
5
5
  import re
6
6
  import textwrap
7
7
  import traceback
8
+ import logging
8
9
  from io import StringIO
9
10
  from tempfile import NamedTemporaryFile, TemporaryDirectory
10
11
 
11
12
  import gevent
12
13
  import requests
13
14
  from pyquery import PyQuery as pq
14
-
15
15
  import locust
16
16
  from locust import constant, LoadTestShape
17
17
  from locust.argument_parser import get_parser, parse_options
@@ -21,6 +21,7 @@ from locust.runners import Runner
21
21
  from locust import stats
22
22
  from locust.stats import StatsCSVFileWriter
23
23
  from locust.web import WebUI
24
+ from locust.log import LogReader
24
25
 
25
26
  from .mock_locustfile import mock_locustfile
26
27
  from .testcases import LocustTestCase
@@ -1013,6 +1014,19 @@ class TestWebUI(LocustTestCase, _HeaderCheckMixin):
1013
1014
  self.assertIn("Script: <span>locust.py</span>", str(d))
1014
1015
  self.assertIn("Target Host: <span>http://localhost</span>", str(d))
1015
1016
 
1017
+ def test_logs(self):
1018
+ log_handler = LogReader()
1019
+ log_handler.name = "log_reader"
1020
+ log_handler.setLevel(logging.INFO)
1021
+ logger = logging.getLogger("root")
1022
+ logger.addHandler(log_handler)
1023
+ log_line = "some log info"
1024
+ logger.info(log_line)
1025
+
1026
+ response = requests.get("http://127.0.0.1:%i/logs" % self.web_port)
1027
+
1028
+ self.assertIn(log_line, response.json().get("logs"))
1029
+
1016
1030
 
1017
1031
  class TestWebUIAuth(LocustTestCase):
1018
1032
  def setUp(self):
locust/web.py CHANGED
@@ -136,6 +136,12 @@ class WebUI:
136
136
  if not delayed_start:
137
137
  self.start()
138
138
 
139
+ @app.errorhandler(Exception)
140
+ def handle_exception(error):
141
+ error_message = str(error)
142
+ logger.log(logging.CRITICAL, error_message)
143
+ return make_response(error_message, 500)
144
+
139
145
  @app.route("/assets/<path:path>")
140
146
  def send_assets(path):
141
147
  webui_build_path = self.webui_build_path
@@ -478,6 +484,20 @@ class WebUI:
478
484
  }
479
485
  return task_data
480
486
 
487
+ @app.route("/logs")
488
+ @self.auth_required_if_enabled
489
+ def logs():
490
+ log_reader_handler = [
491
+ handler for handler in logging.getLogger("root").handlers if handler.name == "log_reader"
492
+ ]
493
+
494
+ if log_reader_handler:
495
+ logs = log_reader_handler[0].logs
496
+ else:
497
+ logs = []
498
+
499
+ return jsonify({"logs": logs})
500
+
481
501
  def start(self):
482
502
  self.greenlet = gevent.spawn(self.start_server)
483
503
  self.greenlet.link_exception(greenlet_exception_handler)