J'utilise python Requests . Je dois déboguer une activité OAuth
et je souhaiterais qu'il enregistre toutes les requêtes en cours d'exécution. I pourrait obtenir cette information avec ngrep
, mais malheureusement, il n’est pas possible de grep des connexions https (nécessaires pour OAuth
)
Comment activer la journalisation de toutes les URL (+ paramètres) auxquelles Requests
accède?
La bibliothèque urllib3
Sous-jacente enregistre toutes les nouvelles connexions et URL avec le corps logging
module , mais pas POST
. Pour GET
demandes, cela devrait suffire:
import logging
logging.basicConfig(level=logging.DEBUG)
qui vous donne l'option de journalisation la plus prolixe; voir le HOWTO sur la journalisation pour plus de détails sur la configuration des niveaux et des destinations de journalisation.
Courte démo:
>>> import requests
>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> r = requests.get('http://httpbin.org/get?foo=bar&baz=python')
INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): httpbin.org
DEBUG:requests.packages.urllib3.connectionpool:"GET /get?foo=bar&baz=python HTTP/1.1" 200 353
Les messages suivants sont enregistrés:
INFO
: Nouvelles connexions (HTTP ou HTTPS)INFO
: connexions interrompuesINFO
: redirectionsWARN
: pool de connexions saturé (si cela se produit, augmentez souvent la taille du pool de connexions)WARN
: Nouvelle tentative de connexionDEBUG
: Détails de la connexion: méthode, chemin, version HTTP, code d'état et longueur de la réponseVous devez activer le débogage au niveau httplib
(requests
→ urllib3
→ httplib
).
Voici quelques fonctions pour basculer (..._on()
et ..._off()
) ou pour l’activer temporairement:
import logging
import contextlib
try:
from http.client import HTTPConnection # py3
except ImportError:
from httplib import HTTPConnection # py2
def debug_requests_on():
'''Switches on logging of the requests module.'''
HTTPConnection.debuglevel = 1
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
def debug_requests_off():
'''Switches off logging of the requests module, might be some side-effects'''
HTTPConnection.debuglevel = 0
root_logger = logging.getLogger()
root_logger.setLevel(logging.WARNING)
root_logger.handlers = []
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.WARNING)
requests_log.propagate = False
@contextlib.contextmanager
def debug_requests():
'''Use with 'with'!'''
debug_requests_on()
yield
debug_requests_off()
Utilisation de la démo:
>>> requests.get('http://httpbin.org/')
<Response [200]>
>>> debug_requests_on()
>>> requests.get('http://httpbin.org/')
INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): httpbin.org
DEBUG:requests.packages.urllib3.connectionpool:"GET / HTTP/1.1" 200 12150
send: 'GET / HTTP/1.1\r\nHost: httpbin.org\r\nConnection: keep-alive\r\nAccept-
Encoding: gzip, deflate\r\nAccept: */*\r\nUser-Agent: python-requests/2.11.1\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: Server: nginx
...
<Response [200]>
>>> debug_requests_off()
>>> requests.get('http://httpbin.org/')
<Response [200]>
>>> with debug_requests():
... requests.get('http://httpbin.org/')
INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): httpbin.org
...
<Response [200]>
Vous verrez la DEMANDE, y compris HEADERS et DATA, et RESPONSE avec HEADERS mais sans DATA. La seule chose qui manquera sera le response.body qui n'est pas connecté.
Pour ceux qui utilisent python 3+
import requests
import logging
import http.client
http.client.HTTPConnection.debuglevel = 1
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
J'utilise python 3.4, demandes 2.19.1:
'urllib3' est l'enregistreur à obtenir maintenant (et non plus 'requests.packages.urllib3'). La journalisation de base aura toujours lieu sans définir http.client.HTTPConnection.debuglevel
Lorsque vous essayez d’obtenir le Python (import logging
) pour émettre des messages de journal de débogage de bas niveau, il m’a surpris de découvrir cela étant donné:
requests --> urllib3 --> http.client.HTTPConnection
seulement ça urlb3
utilise en fait le système Python logging
:
requests
nohttp.client.HTTPConnection
nonurllib3
ouiBien sûr, vous pouvez extraire les messages de débogage à partir de HTTPConnection
en définissant:
HTTPConnection.debuglevel = 1
mais ces sorties sont simplement émises via l'instruction print
. Pour le prouver, simplement grep the Python 3.7 client.py
code source et visualisez vous-même les instructions d'impression (merci @Yohann):
curl https://raw.githubusercontent.com/python/cpython/3.7/Lib/http/client.py |grep -A1 debuglevel`
Il est probable que la redirection de stdout d’une manière ou d’une autre puisse fonctionner sur une stdout en corne de chaussure dans le système de journalisation et éventuellement capturer, par ex. un fichier journal.
urlib3
'enregistreur pas' requests.packages.urllib3
'Capturer urlib3
informations de débogage via le système Python 3 logging
, contrairement à de nombreux conseils sur Internet, et comme @MikeSmith le souligne, vous n'aurez pas beaucoup de chance en interceptant:
log = logging.getLogger('requests.packages.urllib3')
à la place, vous devez:
log = logging.getLogger('urllib3')
url3lib
dans un fichier journalVoici du code qui enregistre url3lib
dans un fichier journal à l’aide du système Python logging
:
import requests
import logging
from http.client import HTTPConnection # py3
# log = logging.getLogger('requests.packages.urllib3') # useless
log = logging.getLogger('urllib3') # works
log.setLevel(logging.DEBUG) # needed
fh = logging.FileHandler("requests.log")
log.addHandler(fh)
requests.get('http://httpbin.org/')
le résultat:
Starting new HTTP connection (1): httpbin.org:80
http://httpbin.org:80 "GET / HTTP/1.1" 200 3168
HTTPConnection.debuglevel
instructions print ()Si vous définissez HTTPConnection.debuglevel = 1
from http.client import HTTPConnection # py3
HTTPConnection.debuglevel = 1
requests.get('http://httpbin.org/')
vous obtiendrez la déclaration print contenant des informations supplémentaires de bas niveau juteuses:
send: b'GET / HTTP/1.1\r\nHost: httpbin.org\r\nUser-Agent: python-
requests/2.22.0\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: Access-Control-Allow-Credentials header: Access-Control-Allow-Origin
header: Content-Encoding header: Content-Type header: Date header: ...
Rappelez-vous que cette sortie utilise print
et non le système Python logging
], et ne peut donc pas être capturée à l'aide d'un gestionnaire de flux ou d'un gestionnaire de fichiers logging
traditionnel (bien qu'il soit possible de capturer la sortie dans un fichier en redirigeant stdout) .
Pour maximiser toute la journalisation possible, vous devez choisir la sortie console/stdout avec ceci:
import requests
import logging
from http.client import HTTPConnection # py3
log = logging.getLogger('urllib3')
log.setLevel(logging.DEBUG)
# logging from urllib3 to console
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
log.addHandler(ch)
# print statements from `http.client.HTTPConnection` to console/stdout
HTTPConnection.debuglevel = 1
requests.get('http://httpbin.org/')
donnant la gamme complète de sortie:
Starting new HTTP connection (1): httpbin.org:80
send: b'GET / HTTP/1.1\r\nHost: httpbin.org\r\nUser-Agent: python-requests/2.22.0\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
http://httpbin.org:80 "GET / HTTP/1.1" 200 3168
header: Access-Control-Allow-Credentials header: Access-Control-Allow-Origin
header: Content-Encoding header: ...