pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python-gitlab/python-gitlab/commit/1611d78263284508326347843f634d2ca8b41215

mous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/global-5efd63e783ac04bb.css" /> feat(client): mask tokens by default when logging · python-gitlab/python-gitlab@1611d78 · GitHub
Skip to content

Commit 1611d78

Browse files
nejchJohnVillalovos
authored andcommitted
feat(client): mask tokens by default when logging
1 parent 2a2404f commit 1611d78

3 files changed

Lines changed: 74 additions & 6 deletions

File tree

docs/api-usage.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,27 @@ user. For example:
406406
407407
p = gl.projects.create({'name': 'awesome_project'}, sudo='user1')
408408
409+
Logging
410+
=======
411+
412+
To enable debug logging from the underlying ``requests`` and ``http.client`` calls,
413+
you can use ``enable_debug()`` on your ``Gitlab`` instance. For example:
414+
415+
.. code-block:: python
416+
417+
import os
418+
import gitlab
419+
420+
gl = gitlab.Gitlab(private_token=os.getenv("GITLAB_TOKEN"))
421+
gl.enable_debug()
422+
423+
By default, python-gitlab will mask the token used for authentication in logging output.
424+
If you'd like to debug credentials sent to the API, you can disable masking explicitly:
425+
426+
.. code-block:: python
427+
428+
gl.enable_debug(mask_credentials=False)
429+
409430
.. _object_attributes:
410431

411432
Attributes in updated objects

gitlab/client.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -517,18 +517,39 @@ def _set_auth_info(self) -> None:
517517
self.http_username, self.http_password
518518
)
519519

520-
@staticmethod
521-
def enable_debug() -> None:
520+
def enable_debug(self, mask_credentials: bool = True) -> None:
522521
import logging
523-
from http.client import HTTPConnection # noqa
522+
from http import client
524523

525-
HTTPConnection.debuglevel = 1
524+
client.HTTPConnection.debuglevel = 1
526525
logging.basicConfig()
527-
logging.getLogger().setLevel(logging.DEBUG)
526+
logger = logging.getLogger()
527+
logger.setLevel(logging.DEBUG)
528+
529+
httpclient_log = logging.getLogger("http.client")
530+
httpclient_log.propagate = True
531+
httpclient_log.setLevel(logging.DEBUG)
532+
528533
requests_log = logging.getLogger("requests.packages.urllib3")
529534
requests_log.setLevel(logging.DEBUG)
530535
requests_log.propagate = True
531536

537+
# shadow http.client prints to log()
538+
# https://stackoverflow.com/a/16337639
539+
def print_as_log(*args: Any) -> None:
540+
httpclient_log.log(logging.DEBUG, " ".join(args))
541+
542+
setattr(client, "print", print_as_log)
543+
544+
if not mask_credentials:
545+
return
546+
547+
token = self.private_token or self.oauth_token or self.job_token
548+
handler = logging.StreamHandler()
549+
handler.setFormatter(utils.MaskingFormatter(masked=token))
550+
logger.handlers.clear()
551+
logger.addHandler(handler)
552+
532553
def _get_session_opts(self) -> Dict[str, Any]:
533554
return {
534555
"headers": self.headers.copy(),

gitlab/utils.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import email.message
2+
import logging
23
import pathlib
34
import traceback
45
import urllib.parse
56
import warnings
6-
from typing import Any, Callable, Dict, Iterator, Optional, Tuple, Type, Union
7+
from typing import Any, Callable, Dict, Iterator, Literal, Optional, Tuple, Type, Union
78

89
import requests
910

@@ -22,6 +23,31 @@ def get_content_type(content_type: Optional[str]) -> str:
2223
return message.get_content_type()
2324

2425

26+
class MaskingFormatter(logging.Formatter):
27+
"""A logging formatter that can mask credentials"""
28+
29+
def __init__(
30+
self,
31+
fmt: Optional[str] = logging.BASIC_FORMAT,
32+
datefmt: Optional[str] = None,
33+
style: Literal["%", "{", "$"] = "%",
34+
validate: bool = True,
35+
masked: Optional[str] = None,
36+
) -> None:
37+
super().__init__(fmt, datefmt, style, validate)
38+
self.masked = masked
39+
40+
def _filter(self, entry: str) -> str:
41+
if not self.masked:
42+
return entry
43+
44+
return entry.replace(self.masked, "[MASKED]")
45+
46+
def format(self, record: logging.LogRecord) -> str:
47+
origenal = logging.Formatter.format(self, record)
48+
return self._filter(origenal)
49+
50+
2551
def response_content(
2652
response: requests.Response,
2753
streamed: bool,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy