파이썬 요청을 사용하고 있습니다. 일부 OAuth
활동 을 디버그해야하며 이를 위해 수행중인 모든 요청을 기록하고 싶습니다. 나는이 정보를 얻을 수 ngrep
있지만, (에 필요한 그렙 HTTPS 연결 할 수 없습니다 불행히도 OAuth
)
Requests
액세스 하는 모든 URL (+ 매개 변수)의 로깅을 활성화하려면 어떻게해야 합니까?
파이썬 요청을 사용하고 있습니다. 일부 OAuth
활동 을 디버그해야하며 이를 위해 수행중인 모든 요청을 기록하고 싶습니다. 나는이 정보를 얻을 수 ngrep
있지만, (에 필요한 그렙 HTTPS 연결 할 수 없습니다 불행히도 OAuth
)
Requests
액세스 하는 모든 URL (+ 매개 변수)의 로깅을 활성화하려면 어떻게해야 합니까?
답변:
기본 urllib3
라이브러리는 본문이 아닌 logging
모듈을 사용하여 모든 새 연결 및 URL을 기록합니다 POST
. 대한 GET
요청이 충분해야한다 :
import logging
logging.basicConfig(level=logging.DEBUG)
가장 자세한 로깅 옵션을 제공합니다. 로깅 수준 및 대상을 구성하는 방법에 대한 자세한 내용 은 logging 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
: 새 연결 (HTTP 또는 HTTPS)DEBUG
: 끊어진 연결DEBUG
: 연결 세부 정보 : 방법, 경로, HTTP 버전, 상태 코드 및 응답 길이DEBUG
: 재시도 횟수 증가여기에는 헤더 나 본문이 포함되지 않습니다. 클래스를 urllib3
사용하여 http.client.HTTPConnection
grunt-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
연결이 모든 디버그 정보를 표준 로거에 출력하므로 다음과 같이 선택됩니다 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
access_token
OAuth 요청에이 표시되지 않습니다 . Linkedin이 무단 요청에 대해 불만을 제기하고 있으며 사용중인 라이브러리 ( rauth
위에있는 requests
)가 해당 토큰을 요청과 함께 보내는 지 확인하고 싶습니다 . 나는 그것을 쿼리 매개 변수로 볼 것으로 예상했지만 요청 헤더에있을 수 있습니까? urllib3
에서 헤더도 표시 하도록하려면 어떻게 해야합니까? 그리고 요청 본문? 간단히 말해서 FULL 요청을 어떻게 볼 수 있습니까?
httplib
. logging
대신 그 라이브러리를 사용하고 싶습니다 . 디버그 출력은 선택한 로그 대상으로 리디렉션하지 않고 stdout에 직접 기록됩니다.
httplib
레벨 ( requests
→ urllib3
→ httplib
) 에서 디버깅을 활성화해야합니다 .
다음은 토글 ( ..._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]>
HEADERS 및 DATA를 포함한 REQUEST 및 HEADERS를 사용한 RESPONSE (데이터 제외)가 표시됩니다. 빠진 유일한 것은 기록되지 않은 response.body입니다.
httplib.HTTPConnection.debuglevel = 1
헤더를 가져 오는 데 사용하는 방법에 대한 통찰력에 감사드립니다. 훌륭합니다! 그러나 나는 logging.basicConfig(level=logging.DEBUG)
다른 5 줄 대신에 동일한 결과를 얻는 것 같습니다 . 내가 뭔가를 놓치고 있습니까? 원한다면 루트 대 urllib3에 대해 다른 로깅 수준을 설정하는 방법이 될 수 있다고 생각합니다.
httplib.HTTPConnection.debuglevel = 2
POST 본문도 인쇄 할 수 있습니다.
httplib.HTTPConnection.debuglevel = 1
Mandible79 @ 충분 $ curl https://raw.githubusercontent.com/python/cpython/master/Lib/http/client.py |grep debuglevel
그것은 항상debuglevel > 0
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
stdout
. 문제의 예 : stackoverflow.com/q/58738195/1090360
Python 로깅 시스템 ( import logging
)에서 낮은 수준의 디버그 로그 메시지를 내보내려고 할 때 다음과 같은 사실을 발견하는 것이 놀랍습니다.
requests --> urllib3 --> http.client.HTTPConnection
urllib3
실제로 Python logging
시스템 만 사용합니다 .
requests
아니http.client.HTTPConnection
아니urllib3
예물론 다음 HTTPConnection
을 설정 하여 디버그 메시지를 추출 할 수 있습니다 .
HTTPConnection.debuglevel = 1
그러나 이러한 출력은 단순히 print
명령문을 통해 방출됩니다 . 이를 증명하려면 Python 3.7 client.py
소스 코드 를 grep 하고 print 문을 직접 확인하십시오 (@Yohann에게 감사드립니다) :
curl https://raw.githubusercontent.com/python/cpython/3.7/Lib/http/client.py |grep -A1 debuglevel`
아마도 어떤 식 으로든 stdout을 리디렉션하는 것은 stdout을 로깅 시스템으로 이동시키고 잠재적으로 예를 들어 로그 파일로 캡처하는 데 작동 할 수 있습니다.
urllib3
'가 아닌 ' requests.packages.urllib3
' 로거를 선택하십시오.인터넷에 대한 많은 조언과는 달리 urllib3
Python 3 logging
시스템을 통해 디버그 정보 를 캡처하려면 @MikeSmith가 지적했듯이 운이 좋지 않습니다.
log = logging.getLogger('requests.packages.urllib3')
대신 다음을 수행해야합니다.
log = logging.getLogger('urllib3')
urllib3
로그 파일로 디버깅다음은 urllib3
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/')
결과:
Starting new HTTP connection (1): httpbin.org:80
http://httpbin.org:80 "GET / HTTP/1.1" 200 3168
HTTPConnection.debuglevel
print () 문 활성화설정하면 HTTPConnection.debuglevel = 1
from http.client import HTTPConnection # py3
HTTPConnection.debuglevel = 1
requests.get('http://httpbin.org/')
추가 육즙이 낮은 수준의 정보에 대한 print 문 출력을 얻을 수 있습니다 .
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
Python logging
시스템이 아닌 사용하므로 기존 logging
스트림 또는 파일 핸들러를 사용하여 캡처 할 수 없습니다. (표준 출력을 리디렉션하여 파일로 출력을 캡처 할 수 있음) .
가능한 모든 로깅을 최대화하려면 다음과 같이 콘솔 / 표준 출력을 설정해야합니다.
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, 헤더, 페이로드 및 상태를 포함하여 정확히 어떤 요청-응답 쌍인지 확인하는 것이 좋습니다. 그리고 일반적으로 모든 곳에서 개별 요청을 구성하는 것은 비현실적입니다. 동시에 단일 (또는 소수의 전문화) 사용을 제안하는 성능 고려 사항이 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 왕복을 기록하는 방법입니다.
위의 로깅이 유용하려면 로깅 레코드 를 이해 하고 추가 하는 특수 로깅 포맷터 가 있을 수 있습니다 . 다음과 같이 보일 수 있습니다.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
쿼리가 많을 때 간단한 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)
둘째, 로깅 구성은 사용에 맞게 조정되어야합니다 logging.handlers.HTTPHandler
(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)
마지막으로 Chronologer 인스턴스를 실행합니다. 예 : 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
그리고 요청을 다시 실행하십시오.
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"사용, 기본 인증 팝업에는 빈 암호 사용)을 열고 "열기"버튼을 클릭하면 다음과 같은 내용이 표시됩니다.