Skip to content
This repository was archived by the owner on Jan 13, 2023. It is now read-only.

Commit 2e82151

Browse files
committed
Integrated logger directly into HttpAdapter.
1 parent 41a691e commit 2e82151

File tree

5 files changed

+92
-55
lines changed

5 files changed

+92
-55
lines changed

examples/shell.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from argparse import ArgumentParser
99
from getpass import getpass as secure_input
10-
from logging import INFO, basicConfig, getLogger
10+
from logging import DEBUG, basicConfig, getLogger
1111
from sys import argv, stderr
1212

1313
from six import text_type
@@ -18,10 +18,10 @@
1818

1919
from iota import __version__
2020
from iota.adapter import resolve_adapter
21-
from iota.adapter.wrappers import LogWrapper, RoutingWrapper
21+
from iota.adapter.wrappers import RoutingWrapper
2222

2323

24-
basicConfig(level=INFO, stream=stderr)
24+
basicConfig(level=DEBUG, stream=stderr)
2525

2626

2727
def main(uri, testnet, pow_uri, debug_requests):
@@ -47,7 +47,9 @@ def main(uri, testnet, pow_uri, debug_requests):
4747

4848
# If ``debug_requests`` is specified, log HTTP requests/responses.
4949
if debug_requests:
50-
adapter_ = LogWrapper(adapter_, getLogger(__name__), INFO)
50+
logger = getLogger(__name__)
51+
logger.setLevel(DEBUG)
52+
adapter_.set_logger(logger)
5153

5254
iota = Iota(adapter_, seed=seed, testnet=testnet)
5355

src/iota/adapter/__init__.py

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from abc import ABCMeta, abstractmethod as abstract_method
77
from collections import deque
88
from inspect import isabstract as is_abstract
9+
from logging import DEBUG, Logger
910
from socket import getdefaulttimeout as get_default_timeout
1011
from typing import Container, Dict, List, Optional, Text, Tuple, Union
1112

@@ -136,6 +137,12 @@ class BaseAdapter(with_metaclass(AdapterMeta)):
136137
Protocols that ``resolve_adapter`` can use to identify this adapter
137138
type.
138139
"""
140+
141+
def __init__(self):
142+
super(BaseAdapter, self).__init__()
143+
144+
self._logger = None # type: Logger
145+
139146
@abstract_method
140147
def send_request(self, payload, **kwargs):
141148
# type: (dict, dict) -> dict
@@ -159,6 +166,24 @@ def send_request(self, payload, **kwargs):
159166
'Not implemented in {cls}.'.format(cls=type(self).__name__),
160167
)
161168

169+
def set_logger(self, logger):
170+
# type: (Logger) -> BaseAdapter
171+
"""
172+
Attaches a logger instance to the adapter.
173+
The adapter will send information about API requests/responses to
174+
the logger.
175+
"""
176+
self._logger = logger
177+
return self
178+
179+
def _log(self, level, message, context=None):
180+
# type: (int, Text, Optional[dict]) -> None
181+
"""
182+
Sends a message to the instance's logger, if configured.
183+
"""
184+
if self._logger:
185+
self._logger.log(level, message, extra={'context': context or {}})
186+
162187

163188
class HttpAdapter(BaseAdapter):
164189
"""
@@ -225,6 +250,9 @@ def node_url(self):
225250

226251
def send_request(self, payload, **kwargs):
227252
# type: (dict, dict) -> dict
253+
kwargs.setdefault('headers', {})
254+
kwargs['headers']['Content-type'] = 'application/json'
255+
228256
response = self._send_http_request(
229257
# Use a custom JSON encoder that knows how to convert Tryte values.
230258
payload = JsonEncoder().encode(payload),
@@ -235,8 +263,7 @@ def send_request(self, payload, **kwargs):
235263

236264
return self._interpret_response(response, payload, {codes['ok']})
237265

238-
@staticmethod
239-
def _send_http_request(url, payload, method='post', **kwargs):
266+
def _send_http_request(self, url, payload, method='post', **kwargs):
240267
# type: (Text, Optional[Text], Text, dict) -> Response
241268
"""
242269
Sends the actual HTTP request.
@@ -245,7 +272,47 @@ def _send_http_request(url, payload, method='post', **kwargs):
245272
tests.
246273
"""
247274
kwargs.setdefault('timeout', get_default_timeout())
248-
return request(method=method, url=url, data=payload, **kwargs)
275+
276+
self._log(
277+
level = DEBUG,
278+
279+
message = 'Sending {method} to {url}: {payload!r}'.format(
280+
method = method,
281+
payload = payload,
282+
url = url,
283+
),
284+
285+
context = {
286+
'request_method': method,
287+
'request_kwargs': kwargs,
288+
'request_payload': payload,
289+
'request_url': url,
290+
},
291+
)
292+
293+
response = request(method=method, url=url, data=payload, **kwargs)
294+
295+
self._log(
296+
level = DEBUG,
297+
298+
message = 'Receiving {method} from {url}: {response!r}'.format(
299+
method = method,
300+
response = response.content,
301+
url = url,
302+
),
303+
304+
context = {
305+
'request_method': method,
306+
'request_kwargs': kwargs,
307+
'request_payload': payload,
308+
'request_url': url,
309+
310+
'response_headers': response.headers,
311+
'response_content': response.content,
312+
},
313+
)
314+
315+
return response
249316

250317
def _interpret_response(self, response, payload, expected_status):
251318
# type: (Response, dict, Container[int]) -> dict

src/iota/adapter/wrappers.py

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@
33
unicode_literals
44

55
from abc import ABCMeta, abstractmethod as abstract_method
6-
from logging import INFO, Logger
76
from typing import Dict, Text
87

9-
from six import with_metaclass
10-
118
from iota.adapter import AdapterSpec, BaseAdapter, resolve_adapter
9+
from six import with_metaclass
1210

1311
__all__ = [
14-
'LogWrapper',
1512
'RoutingWrapper',
1613
]
1714

@@ -38,41 +35,6 @@ def send_request(self, payload, **kwargs):
3835
)
3936

4037

41-
class LogWrapper(BaseWrapper):
42-
"""
43-
Wrapper that sends all adapter requests and responses to a logger.
44-
45-
To use it, "wrap" the real adapter instance/spec::
46-
47-
logger = getLogger('...')
48-
api = Iota(LogWrapper('http://localhost:14265', logger))
49-
"""
50-
def __init__(self, adapter, logger, level=INFO):
51-
# type: (AdapterSpec, Logger, int) -> None
52-
super(LogWrapper, self).__init__(adapter)
53-
54-
self.logger = logger
55-
self.level = level
56-
57-
def send_request(self, payload, **kwargs):
58-
# type: (dict, dict) -> dict
59-
command = payload.get('command') or 'command'
60-
61-
self.logger.log(self.level, 'Sending {command}: {request!r}'.format(
62-
command = command,
63-
request = payload,
64-
))
65-
66-
response = self.adapter.send_request(payload, **kwargs)
67-
68-
self.logger.log(self.level, 'Receiving {command}: {response!r}'.format(
69-
command = command,
70-
response = response,
71-
))
72-
73-
return response
74-
75-
7638
class RoutingWrapper(BaseWrapper):
7739
"""
7840
Routes commands to different nodes.

test/adapter/sandbox_test.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ def test_regular_command(self):
4242

4343
# Auth token automatically added to the HTTP request.
4444
headers = {
45-
'Authorization': 'token ACCESS-TOKEN',
45+
'Authorization': 'token ACCESS-TOKEN',
46+
'Content-type': 'application/json',
4647
},
4748
)
4849

@@ -229,10 +230,11 @@ def test_regular_command_null_token(self):
229230
payload = json.dumps(payload),
230231
url = adapter.node_url,
231232

232-
# No auth token, so no Authorization header.
233-
# headers = {
234-
# 'Authorization': 'token ACCESS-TOKEN',
235-
# },
233+
headers = {
234+
# No auth token, so no Authorization header.
235+
# 'Authorization': 'token ACCESS-TOKEN',
236+
'Content-type': 'application/json',
237+
},
236238
)
237239

238240
def test_error_non_200_response(self):

test/adapter_test.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ def test_adapter_instance(self):
2626

2727
def test_http(self):
2828
"""
29-
Resolving a valid `http://` URI.
29+
Resolving a valid ``http://`` URI.
3030
"""
3131
adapter = resolve_adapter('http://localhost:14265/')
3232
self.assertIsInstance(adapter, HttpAdapter)
3333

3434
def test_https(self):
3535
"""
36-
Resolving a valid `https://` URI.
36+
Resolving a valid ``https://`` URI.
3737
"""
3838
adapter = resolve_adapter('https://localhost:14265/')
3939
self.assertIsInstance(adapter, HttpAdapter)
@@ -73,7 +73,7 @@ def create_http_response(content, status=200):
7373
class HttpAdapterTestCase(TestCase):
7474
def test_http(self):
7575
"""
76-
Configuring HttpAdapter using a valid http:// URI.
76+
Configuring HttpAdapter using a valid ``http://`` URI.
7777
"""
7878
uri = 'http://localhost:14265/'
7979
adapter = HttpAdapter(uri)
@@ -82,7 +82,7 @@ def test_http(self):
8282

8383
def test_https(self):
8484
"""
85-
Configuring HttpAdapter using a valid https:// URI.
85+
Configuring HttpAdapter using a valid ``https://`` URI.
8686
"""
8787
uri = 'https://localhost:14265/'
8888
adapter = HttpAdapter(uri)
@@ -338,4 +338,8 @@ def test_trytes_in_request():
338338
'CCPCBDVC9DTCEAKDXC9D9DEARCWCPCBDVCTCEAHDWCTCEAKDCDFD9DSCSA',
339339
],
340340
}),
341+
342+
headers = {
343+
'Content-type': 'application/json',
344+
},
341345
)

0 commit comments

Comments
 (0)