Memiliki skrip atau bahkan subsistem aplikasi untuk debugging protokol jaringan, diinginkan untuk melihat apa pasangan permintaan-respons sebenarnya, termasuk URL, header, payloads dan status yang efektif. Dan biasanya tidak praktis untuk melengkapi permintaan individu di semua tempat. Pada saat yang sama ada pertimbangan kinerja yang menyarankan penggunaan tunggal (atau beberapa khusus) requests.Session
, jadi berikut ini mengasumsikan bahwa saran itu diikuti.
requests
mendukung apa yang disebut event hooks (pada 2.23 sebenarnya hanya ada response
hook). Ini pada dasarnya adalah pendengar acara, dan acara tersebut dipancarkan sebelum mengembalikan kontrol dari requests.request
. Pada saat ini baik permintaan maupun tanggapan telah ditentukan sepenuhnya, sehingga dapat dicatat.
import logging
import requests
logger = logging.getLogger('httplogger')
def logRoundtrip(response, *args, **kwargs):
extra = {'req': response.request, 'res': response}
logger.debug('HTTP roundtrip', extra=extra)
session = requests.Session()
session.hooks['response'].append(logRoundtrip)
Itu pada dasarnya cara mencatat semua perjalanan bolak-balik HTTP dari sebuah sesi.
Memformat catatan log bolak-balik HTTP
Agar logging di atas bermanfaat, mungkin ada pemformat logging khusus yang memahami req
dan res
ekstra pada catatan logging. Ini bisa terlihat seperti ini:
import textwrap
class HttpFormatter(logging.Formatter):
def _formatHeaders(self, d):
return '\n'.join(f'{k}: {v}' for k, v in d.items())
def formatMessage(self, record):
result = super().formatMessage(record)
if record.name == 'httplogger':
result += textwrap.dedent('''
---------------- request ----------------
{req.method} {req.url}
{reqhdrs}
{req.body}
---------------- response ----------------
{res.status_code} {res.reason} {res.url}
{reshdrs}
{res.text}
''').format(
req=record.req,
res=record.res,
reqhdrs=self._formatHeaders(record.req.headers),
reshdrs=self._formatHeaders(record.res.headers),
)
return result
formatter = HttpFormatter('{asctime} {levelname} {name} {message}', style='{')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logging.basicConfig(level=logging.DEBUG, handlers=[handler])
Sekarang jika Anda melakukan beberapa permintaan menggunakan session
, seperti:
session.get('https://httpbin.org/user-agent')
session.get('https://httpbin.org/status/200')
Outputnya stderr
akan terlihat sebagai berikut.
2020-05-14 22:10:13,224 DEBUG urllib3.connectionpool Starting new HTTPS connection (1): httpbin.org:443
2020-05-14 22:10:13,695 DEBUG urllib3.connectionpool https://httpbin.org:443 "GET /user-agent HTTP/1.1" 200 45
2020-05-14 22:10:13,698 DEBUG httplogger HTTP roundtrip
---------------- request ----------------
GET https://httpbin.org/user-agent
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
None
---------------- response ----------------
200 OK https://httpbin.org/user-agent
Date: Thu, 14 May 2020 20:10:13 GMT
Content-Type: application/json
Content-Length: 45
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
{
"user-agent": "python-requests/2.23.0"
}
2020-05-14 22:10:13,814 DEBUG urllib3.connectionpool https://httpbin.org:443 "GET /status/200 HTTP/1.1" 200 0
2020-05-14 22:10:13,818 DEBUG httplogger HTTP roundtrip
---------------- request ----------------
GET https://httpbin.org/status/200
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
None
---------------- response ----------------
200 OK https://httpbin.org/status/200
Date: Thu, 14 May 2020 20:10:13 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Cara GUI
Saat Anda memiliki banyak kueri, memiliki UI sederhana dan cara untuk memfilter catatan akan berguna. Saya akan menunjukkan untuk menggunakan Chronologer untuk itu (saya adalah penulisnya).
Pertama, hook telah ditulis ulang untuk menghasilkan rekaman yang logging
dapat diserialisasi saat dikirim melalui kabel. Ini bisa terlihat seperti ini:
def logRoundtrip(response, *args, **kwargs):
extra = {
'req': {
'method': response.request.method,
'url': response.request.url,
'headers': response.request.headers,
'body': response.request.body,
},
'res': {
'code': response.status_code,
'reason': response.reason,
'url': response.url,
'headers': response.headers,
'body': response.text
},
}
logger.debug('HTTP roundtrip', extra=extra)
session = requests.Session()
session.hooks['response'].append(logRoundtrip)
Kedua, konfigurasi logging harus disesuaikan untuk digunakan logging.handlers.HTTPHandler
(yang dipahami oleh Chronologer).
import logging.handlers
chrono = logging.handlers.HTTPHandler(
'localhost:8080', '/api/v1/record', 'POST', credentials=('logger', ''))
handlers = [logging.StreamHandler(), chrono]
logging.basicConfig(level=logging.DEBUG, handlers=handlers)
Terakhir, jalankan instance Chronologer. misal menggunakan Docker:
docker run --rm -it -p 8080:8080 -v /tmp/db \
-e CHRONOLOGER_STORAGE_DSN=sqlite:////tmp/db/chrono.sqlite \
-e CHRONOLOGER_SECRET=example \
-e CHRONOLOGER_ROLES="basic-reader query-reader writer" \
saaj/chronologer \
python -m chronologer -e production serve -u www-data -g www-data -m
Dan jalankan permintaan lagi:
session.get('https://httpbin.org/user-agent')
session.get('https://httpbin.org/status/200')
Pengendali aliran akan menghasilkan:
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): httpbin.org:443
DEBUG:urllib3.connectionpool:https://httpbin.org:443 "GET /user-agent HTTP/1.1" 200 45
DEBUG:httplogger:HTTP roundtrip
DEBUG:urllib3.connectionpool:https://httpbin.org:443 "GET /status/200 HTTP/1.1" 200 0
DEBUG:httplogger:HTTP roundtrip
Sekarang jika Anda membuka http: // localhost: 8080 / (gunakan "logger" untuk nama pengguna dan kata sandi kosong untuk popup auth dasar) dan klik tombol "Open", Anda akan melihat sesuatu seperti: