Skip to content

Commit a765e43

Browse files
author
frizzby
committed
Merge remote-tracking branch 'vasili/log_attachments' into async_requests
2 parents d00830a + efd6df4 commit a765e43

File tree

4 files changed

+73
-13
lines changed

4 files changed

+73
-13
lines changed

README.rst

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ logging handler privided by plugin like bellow:
6060
.. code-block:: python
6161
6262
import logging
63-
# Import Report Portal handler to the test module.
64-
from pytest_reportportal import RPlogHandler
63+
# Import Report Portal logger and handler to the test module.
64+
from pytest_reportportal import RPLogger, RPLogHandler
6565
# Setting up a logging.
66+
logging.setLoggerClass(RPLogger)
6667
logger = logging.getLogger(__name__)
6768
logger.setLevel(logging.DEBUG)
6869
# Create handler for Report Portal.
@@ -79,6 +80,19 @@ logging handler privided by plugin like bellow:
7980
x = "this"
8081
logger.info("x is: %s", x)
8182
assert 'h' in x
83+
84+
# Message with an attachment.
85+
import subprocess
86+
free_memory = subprocess.check_output("free -h".split())
87+
logger.info(
88+
"Case1. Memory consumption",
89+
attachment={
90+
"name": "free_memory.txt",
91+
"data": free_memory,
92+
"mime": "application/octet-stream",
93+
},
94+
)
95+
8296
# This debug message will not be sent to the Report Portal.
8397
logger.debug("Case1. Debug message")
8498
@@ -98,4 +112,4 @@ Copyright Notice
98112

99113
Licensed under the GPLv3_ license (see the LICENSE file).
100114

101-
.. _GPLv3: https://www.gnu.org/licenses/quick-guide-gplv3.html
115+
.. _GPLv3: https://www.gnu.org/licenses/quick-guide-gplv3.html

pytest_reportportal/__init__.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,42 @@
1+
import sys
12
import logging
23

34
from .service import PyTestService
45

56

6-
class RPlogHandler(logging.Handler):
7+
class RPLogger(logging.getLoggerClass()):
8+
9+
def __init__(self, name, level=0):
10+
super(RPLogger, self).__init__(name, level=level)
11+
12+
def _log(self, level, msg, args, exc_info=None, extra=None,
13+
attachment=None):
14+
"""
15+
Low-level logging routine which creates a LogRecord and then calls
16+
all the handlers of this logger to handle the record.
17+
"""
18+
if logging._srcfile:
19+
# IronPython doesn't track Python frames, so findCaller raises an
20+
# exception on some versions of IronPython. We trap it here so that
21+
# IronPython can use logging.
22+
try:
23+
fn, lno, func = self.findCaller()
24+
except ValueError:
25+
fn, lno, func = "(unknown file)", 0, "(unknown function)"
26+
else:
27+
fn, lno, func = "(unknown file)", 0, "(unknown function)"
28+
29+
if exc_info and not isinstance(exc_info, tuple):
30+
exc_info = sys.exc_info()
31+
32+
record = self.makeRecord(
33+
self.name, level, fn, lno, msg, args, exc_info, func, extra
34+
)
35+
record.attachment = attachment
36+
self.handle(record)
37+
38+
39+
class RPLogHandler(logging.Handler):
740

841
# Map loglevel codes from `logging` module to ReportPortal text names:
942
_loglevel_map = {
@@ -17,7 +50,7 @@ class RPlogHandler(logging.Handler):
1750
_sorted_levelnos = sorted(_loglevel_map.keys(), reverse=True)
1851

1952
def __init__(self, level=logging.NOTSET):
20-
super(RPlogHandler, self).__init__(level)
53+
super(RPLogHandler, self).__init__(level)
2154

2255
def emit(self, record):
2356
try:
@@ -31,4 +64,8 @@ def emit(self, record):
3164
if level <= record.levelno:
3265
break
3366

34-
return PyTestService.post_log(msg, loglevel=self._loglevel_map[level])
67+
return PyTestService.post_log(
68+
msg,
69+
loglevel=self._loglevel_map[level],
70+
attachment=record.__dict__.get("attachment", None),
71+
)

pytest_reportportal/plugin.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ def pytest_runtest_makereport(self, item, call):
2323
report = (yield).get_result()
2424

2525
if report.longrepr:
26-
PyTestService.post_log(cgi.escape(report.longreprtext), loglevel='ERROR')
26+
PyTestService.post_log(
27+
cgi.escape(report.longreprtext),
28+
loglevel='ERROR',
29+
)
2730

2831
if report.when == "setup":
2932

pytest_reportportal/service.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
from reportportal_client import ReportPortalServiceAsync
55

66

7+
def async_error_handler(exception):
8+
raise exception
9+
10+
711
def timestamp():
812
return str(int(time() * 1000))
913

@@ -34,7 +38,8 @@ def init_service(self, endpoint, project, uuid):
3438
self.RP = ReportPortalServiceAsync(
3539
endpoint=endpoint,
3640
project=project,
37-
token=uuid)
41+
token=uuid,
42+
error_handler=async_error_handler)
3843
else:
3944
logging.debug("The pytest is already initialized")
4045
return self.RP
@@ -103,17 +108,18 @@ def finish_launch(self, launch=None, status="rp_launch"):
103108
"request_body=%s", fl_rq)
104109
self.RP.finish_launch(**fl_rq)
105110

106-
def post_log(self, message, loglevel='INFO'):
111+
def post_log(self, message, loglevel='INFO', attachment=None):
107112
if loglevel not in self._loglevels:
108-
logging.warning(
109-
'Incorrect loglevel = %s. Force set to INFO. Avaliable levels:'
110-
' %s.', loglevel, self._loglevels)
113+
logging.warning('Incorrect loglevel = {}. Force set to INFO. '
114+
'Avaliable levels: {}.'
115+
.format(loglevel, self._loglevels))
111116
loglevel = 'INFO'
112117

113118
sl_rq = {
114119
"time": timestamp(),
115120
"message": message,
116-
"level": loglevel
121+
"level": loglevel,
122+
"attachment": attachment,
117123
}
118124
self.RP.log(**sl_rq)
119125

0 commit comments

Comments
 (0)