programing

python-requests 모듈의 모든 요청 기록

topblog 2023. 7. 17. 20:31
반응형

python-requests 모듈의 모든 요청 기록

저는 python Requests를 사용하고 있습니다.디버그해야 합니다.OAuth활동, 그리고 그것을 위해 저는 수행 중인 모든 요청을 기록하기를 원합니다.는 이 를 저는이정얻수있다니습었을를로 얻을 수 .ngrep연결을 수 연결에 함).OAuth)

다음과 같은 모든 URL(+ 매개 변수)의 로깅을 활성화하려면 어떻게 해야 합니까?Requests에 액세스하고 있습니까?

다음에서 디버깅을 사용하도록 설정해야 합니다.httplib레벨)requestsurllib3httplib).

두 가지 토글할 수 있는 기능입니다...._on()그리고...._off() 과 같이

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()

데모 사용:

>>> 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]>

헤더와 데이터를 포함한 요청과 데이터가 없는 헤더로 응답을 볼 수 있습니다.유일하게 누락된 것은 응답입니다.기록되지 않은 본문.

원천

가 되는 밑에있.urllib3라이브러리는 모듈과 함께 모든 새 연결 및 URL을 기록하지만 기록하지는 않습니다.POST시체들위해서GET충분해야 .

import logging

logging.basicConfig(level=logging.DEBUG)

가장 자세한 로깅 옵션을 제공합니다. 로깅 수준 및 대상을 구성하는 방법에 대한 자세한 내용은 로깅 HOWTO를 참조하십시오.

간단한 데모:

>>> import requests
>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> r = requests.get('http://httpbin.org/get?foo=bar&baz=python')
DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): httpbin.org:80
DEBUG:urllib3.connectionpool:http://httpbin.org:80 "GET /get?foo=bar&baz=python HTTP/1.1" 200 366

urllib3의 정확한 버전에 따라 다음 메시지가 기록됩니다.

  • INFO
  • WARN연결 풀이 꽉 찼습니다(연결 풀 크기가 자주 증가하는 경우).
  • WARN헤더를 구문 분석하지 못했습니다(잘못된 형식의 응답 헤더).
  • WARN 중입니다.
  • WARN가 예상된 이름과 . 인증서가 호스트 이름과 일치하지 않습니다.
  • WARN할 때 된 응답은 "Content-Length "Transfer-Encoding입니다.
  • DEBUG HTTPS - (HTTP 또는 HTTPS)
  • DEBUG이 끊겼습니다. 연결이 끊어졌습니다.
  • DEBUG: method, 버전, 및 response length : 방법, , 상전, 태연코, 응드및길
  • DEBUG가 증가합니다. 즉, 재시도 횟수가 증가합니다.

여기에는 머리글이나 본문이 포함되지 않습니다. urllib3를 사용합니다.http.client.HTTPConnectiongrunt-work를 수행하는 클래스이지만 해당 클래스는 로깅을 지원하지 않으므로 일반적으로 stdout으로 인쇄하도록 구성할 수 있습니다.그러나 다른 방법을 사용하여 모든 디버그 정보를 로깅으로 전송하도록 조작할 수 있습니다.print해당 모듈에 이름을 입력합니다.

import logging
import http.client

httpclient_logger = logging.getLogger("http.client")

def httpclient_logging_patch(level=logging.DEBUG):
    """Enable HTTPConnection debug logging to the logging framework"""

    def httpclient_log(*args):
        httpclient_logger.log(level, " ".join(args))

    # mask the print() built-in in the http.client module to use
    # logging instead
    http.client.print = httpclient_log
    # enable debugging
    http.client.HTTPConnection.debuglevel = 1

하기 르기httpclient_logging_patch() 인들원http.client 정보를 하기 위한 (연결)를합니다. 즉, 모든 디 버 그 출 기 연 에 의 선 니 됩 택 다 해 서 그 래 결 위 한 하 력 정 로 를 보 거 표 로 준 ▁connect ▁up ▁by ▁picked , 니 다 ▁inform ▁to ▁are 모 ▁logger ions ▁debug ▁output 됩 선logging.basicConfig():

>>> httpclient_logging_patch()
>>> r = requests.get('http://httpbin.org/get?foo=bar&baz=python')
DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): httpbin.org:80
DEBUG:http.client:send: b'GET /get?foo=bar&baz=python 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'
DEBUG:http.client:reply: 'HTTP/1.1 200 OK\r\n'
DEBUG:http.client:header: Date: Tue, 04 Feb 2020 13:36:53 GMT
DEBUG:http.client:header: Content-Type: application/json
DEBUG:http.client:header: Content-Length: 366
DEBUG:http.client:header: Connection: keep-alive
DEBUG:http.client:header: Server: gunicorn/19.9.0
DEBUG:http.client:header: Access-Control-Allow-Origin: *
DEBUG:http.client:header: Access-Control-Allow-Credentials: true
DEBUG:urllib3.connectionpool:http://httpbin.org:80 "GET /get?foo=bar&baz=python HTTP/1.1" 200 366

파이썬 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

Python 로깅 시스템을 가져오려고 할 때 (import logging메시지를 은 주어진 의 디 다 것 같 보 것 발 고 놀 하 습 랐 견 을 은 과

requests --> urllib3 --> http.client.HTTPConnection

urllib3는 Python 을 합니다.logging시스템:

  • requests 아니요.
  • http.client.HTTPConnection 아니요.
  • urllib3 네.

는 예, 다에서추수있다에서 추출할 수 .HTTPConnection설정 기준:

HTTPConnection.debuglevel = 1

하지만 이 출력들은 단지 그것을 통해 방출됩니다.print하기 위해 Python 3.7 Grep을 사용하면 됩니다.client.py소스 코드와 인쇄문을 직접 확인합니다(감사합니다 @요한).

curl https://raw.githubusercontent.com/python/cpython/3.7/Lib/http/client.py |grep -A1 debuglevel` 

stdout을 어떤 식으로든 리디렉션하면 stdout을 로깅 시스템에 적용하고 로그 파일 등에 캡처할 수 있습니다.

를선합니다택다'''를 선택합니다.urllib3 not 거님아로'requests.packages.urllib3'

캡처하기urllib3 3 Python 는 다음과 .logging@MikeSmith가 지적했듯이, 시스템은 인터넷에 대한 많은 조언과는 달리, 가로채는 행운이 많지 않을 것입니다.

log = logging.getLogger('requests.packages.urllib3')

대신 다음을 수행해야 합니다.

log = logging.getLogger('urllib3')

디버깅 디urllib3

여기 기록되는 코드가 있습니다.urllib3Python 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/')

결과:

Starting new HTTP connection (1): httpbin.org:80
http://httpbin.org:80 "GET / HTTP/1.1" 200 3168

사용 HTTPConnection.debuglevel를 발행합니다.

설정하는 경우HTTPConnection.debuglevel = 1

from http.client import HTTPConnection  # py3
HTTPConnection.debuglevel = 1
requests.get('http://httpbin.org/')

다음과 같은 추가적인 저수준 정보의 인쇄 명세서 출력을 얻을 수 있습니다.

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

은 " " " " 를 사용합니다." 을 사용합니다.print그리고 파이썬이 아닙니다.logging시스템, 따라서 전통적인 시스템을 사용하여 캡처할 수 없습니다.logging스트림 또는 파일 핸들러(stdout을 리디렉션하여 파일로 출력을 캡처할 수도 있음).

위의 두 가지를 결합하여 콘솔에 가능한 모든 로깅을 최대화합니다.

가능한 모든 로깅을 최대화하려면 다음을 사용하여 콘솔/stdout 출력에 만족해야 합니다.

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/')

전체 출력 범위 제공:

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

네트워크 프로토콜 디버깅을 위한 스크립트 또는 애플리케이션의 하위 시스템이 있으므로, 유효 URL, 헤더, 페이로드 및 상태를 포함하여 요청-응답 쌍이 정확히 무엇인지 확인해야 합니다.일반적으로 개별적인 요청을 모든 곳에 적용하는 것은 비현실적입니다.의 전문화된)는▁single▁(▁special▁at▁using▁considerance다▁suggest있▁thatations▁few니ised)or습또▁perform▁are사▁there▁the항(▁same▁time동)를 사용할 것을 제안하는 성능 고려 사항들이 있습니다.requests.Session따라서 다음은 제안이 따르는 것으로 가정합니다.

requests이벤트 후크라고 불리는 것을 지원합니다(2.23 기준).response는 크후)에서 하기 전에 합니다. 기본적으로 이벤트 수신기이며, 이벤트는 에서 제어권을 반환하기 전에 방출됩니다.requests.request이 시점에서 요청과 응답이 모두 완전히 정의되므로 기록할 수 있습니다.

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)

이것이 기본적으로 세션의 모든 HTTP 왕복을 기록하는 방법입니다.

HTTP 왕복 로그 레코드 형식 지정

위의 로깅이 유용하려면 다음을 이해하는 전문 로깅 포맷이 있을 수 있습니다.req그리고.res벌목 기록에 대한 추가 정보.다음과 같이 표시될 수 있습니다.

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])

를 사용하여 을 하면 .session 예:

session.get('https://httpbin.org/user-agent')
session.get('https://httpbin.org/status/200')

대로 됩니다.stderr다음과 같이 표시됩니다.

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

GUI 방식

쿼리가 많을 때는 간단한 UI와 레코드를 필터링할 수 있는 방법이 편리합니다.(저의 저자인) Chronologer를 사용하는 것을 보여드리겠습니다.

첫째, 후크는 다음과 같은 기록을 만들기 위해 다시 작성되었습니다.logging유선으로 보낼 때 직렬화될 수 있습니다.다음과 같이 표시될 수 있습니다.

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)

둘째, 로깅 구성을 사용할 수 있도록 조정해야 합니다(크로놀로거는 이를 이해합니다).

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)

마지막으로 Docker를 사용하여 Chronologer 인스턴스를 실행합니다.

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

요청을 다시 실행합니다.

session.get('https://httpbin.org/user-agent')
session.get('https://httpbin.org/status/200')

스트림 처리기는 다음을 생성합니다.

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

이제 http://localhost:8080/(기본 인증 팝업에 사용자 이름은 "logger" 사용)를 열고 "Open" 버튼을 클릭하면 다음과 같은 메시지가 나타납니다.

Screenshot of Chronologer

이 답변을 개선하는 중입니다.

저는 이렇게 생각했습니다.

import logging
import sys    
import requests
import textwrap
    
root = logging.getLogger('httplogger')


def logRoundtrip(response, *args, **kwargs):
    extra = {'req': response.request, 'res': response}
    root.debug('HTTP roundtrip', extra=extra)
    

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(sys.stdout)
handler.setFormatter(formatter)
root.addHandler(handler)
root.setLevel(logging.DEBUG)


session = requests.Session()
session.hooks['response'].append(logRoundtrip)
session.get('http://httpbin.org')

파이썬 3.4를 사용하고 있습니다. 요청 사항 2.19.1:

'urllib3'이(가) 지금 가져올 로거입니다(더 이상 'urllib3'이 아님).기본 로깅은 http.client를 설정하지 않고 계속 수행됩니다.HTTPConnection.debug 수준

사용 중입니다.logger_config.yaml로깅을 위해 할 은 를 추가하는 이었습니다.disable_existing_loggers: False끝까지

내 로깅 설정은 상당히 광범위하고 혼란스러워서 여기서 설명할 수 있는 좋은 방법조차 모르지만 누군가가 YAML 파일을 사용하여 로깅을 구성하고 있다면 도움이 될 수 있습니다.

https://docs.python.org/3/howto/logging.html#configuring-logging

언급URL : https://stackoverflow.com/questions/16337511/log-all-requests-from-the-python-requests-module

반응형