web-dev-qa-db-fra.com

Journalise toutes les requêtes du module python-request

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?

72
dangonfast

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 interrompues
  • INFO: redirections
  • WARN: pool de connexions saturé (si cela se produit, augmentez souvent la taille du pool de connexions)
  • WARN: Nouvelle tentative de connexion
  • DEBUG: Détails de la connexion: méthode, chemin, version HTTP, code d'état et longueur de la réponse
66
Martijn Pieters

Vous devez activer le débogage au niveau httplib (requestsurllib3httplib).

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é.

Source

97
Yohann

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
35
forrestj

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

2
Mike Smith

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:

  • requestsno
  • http.client.HTTPConnectionnon
  • urllib3oui

Bien 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.

Choisir la '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')

Débogage url3lib dans un fichier journal

Voici 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

Activer le 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) .

Combinez les deux éléments ci-dessus - optimisez toutes les connexions possibles à la console

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: ...
2
abulka