Skip to content

Commit

Permalink
add ability to easily toggle debug logging on/off (#1411)
Browse files Browse the repository at this point in the history
  • Loading branch information
ddonukis authored Oct 12, 2023
1 parent 5c3ba44 commit c54fbc6
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 18 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ Changes are grouped as follows
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.

## [6.32.3] - 2023-10-06
### Added
- Ability to toggle the SDK debug logging on/off by setting `config.debug` property on a CogniteClient to True (enable) or False (disable).

## [6.32.2] - 2023-10-10
### Added
- The credentials class used in TransformationsAPI, `OidcCredentials`, now also accepts `scopes` as a list of strings
Expand Down
2 changes: 1 addition & 1 deletion cognite/client/_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
from cognite.client import CogniteClient
from cognite.client.config import ClientConfig

log = logging.getLogger("cognite-sdk")
log = logging.getLogger(__name__)

T = TypeVar("T")

Expand Down
2 changes: 1 addition & 1 deletion cognite/client/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from __future__ import annotations

__version__ = "6.32.2"
__version__ = "6.32.3"
__api_subversion__ = "V20220125"
20 changes: 16 additions & 4 deletions cognite/client/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,9 @@ def __init__(
self.headers = headers or {}
self.timeout = timeout or 30
self.file_transfer_timeout = file_transfer_timeout or 600
self.debug = debug

if debug:
from cognite.client.utils._logging import _configure_logger_for_debug_mode

_configure_logger_for_debug_mode()
self.debug = True

if not global_config.disable_pypi_version_check:
with suppress(Exception): # PyPI might be unreachable, if so, skip version check
Expand All @@ -96,6 +93,21 @@ def __init__(
_check_client_has_newest_major_version()
self._validate_config()

@property
def debug(self) -> bool:
from cognite.client.utils._logging import _is_debug_logging_enabled

return _is_debug_logging_enabled()

@debug.setter
def debug(self, value: bool) -> None:
from cognite.client.utils._logging import _configure_logger_for_debug_mode, _disable_debug_logging

if value:
_configure_logger_for_debug_mode()
else:
_disable_debug_logging()

def _validate_config(self) -> None:
if not self.project:
raise ValueError(f"Invalid value for ClientConfig.project: <{self.project}>")
Expand Down
9 changes: 4 additions & 5 deletions cognite/client/data_classes/transformations/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

import logging
import warnings
from abc import abstractmethod
from copy import deepcopy
from typing import TYPE_CHECKING, Any, Awaitable, Literal, cast
Expand Down Expand Up @@ -33,9 +33,6 @@
from cognite.client import CogniteClient


logger = logging.getLogger(__name__)


class SessionDetails:
"""Details of a source session.
Expand Down Expand Up @@ -267,10 +264,12 @@ def _try_get_or_create_nonce(
# This is fine, we might be missing SessionsACL. The OIDC credentials will then be passed
# directly to the backend service. We do however not catch CogniteAuthError as that would
# mean the credentials are invalid.
logger.debug(
msg = (
f"Unable to create a session and get a nonce towards {project=} using the provided "
f"{credentials_name} credentials: {err!r}"
"\nProvided OIDC credentials will be passed on to the transformation service."
)
warnings.warn(msg, UserWarning)
return ret

def run(self, wait: bool = True, timeout: float | None = None) -> TransformationJob:
Expand Down
17 changes: 15 additions & 2 deletions cognite/client/utils/_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import logging

_COGNITE_LOGGER_NAME = "cognite.client"


class DebugLogFormatter(logging.Formatter):
RESERVED_ATTRS = (
Expand Down Expand Up @@ -62,11 +64,22 @@ def format(self, record: logging.LogRecord) -> str:


def _configure_logger_for_debug_mode() -> None:
logger = logging.getLogger("cognite-sdk")
logger.setLevel("DEBUG")
logger = logging.getLogger(_COGNITE_LOGGER_NAME)
logger.setLevel(logging.DEBUG)
log_handler = logging.StreamHandler()
formatter = DebugLogFormatter()
log_handler.setFormatter(formatter)
logger.handlers = []
logger.propagate = False
logger.addHandler(log_handler)


def _disable_debug_logging() -> None:
logger = logging.getLogger(_COGNITE_LOGGER_NAME)
logger.setLevel(logging.INFO)
logger.handlers = []


def _is_debug_logging_enabled() -> bool:
logger = logging.getLogger(_COGNITE_LOGGER_NAME)
return logger.isEnabledFor(logging.DEBUG) and logger.hasHandlers()
3 changes: 1 addition & 2 deletions docs/source/cognite_client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ CogniteClient
:members:
:member-order: bysource

.. _class_client_ClientConfig:
.. autoclass:: cognite.client.config.ClientConfig
:members:
:member-order: bysource

.. autoclass:: cognite.client.config.GlobalConfig
:members:
:member-order: bysource


2 changes: 1 addition & 1 deletion docs/source/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Instantiate a new client
Use this code to instantiate a client and get your login status. CDF returns an object with
attributes that describe which project and service account your API key belongs to. The :code:`client_name`
is a user-defined string intended to give the client a unique identifier. You
can provide the :code:`client_name` by passing it directly to the :code:`ClientConfig` constructor.
can provide the :code:`client_name` by passing it directly to the :ref:`ClientConfig <class_client_ClientConfig>` constructor.

The Cognite API uses OpenID Connect (OIDC) to authenticate.
Use one of the credential providers such as OAuthClientCredentials to authenticate:
Expand Down
22 changes: 22 additions & 0 deletions docs/source/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,25 @@ to the API, you can either pass the :code:`max_workers` attribute when you insta
If you are working with multiple instances of :ref:`cognite_client:CogniteClient`, all instances will share the same connection pool.
If you have several instances, you can increase the max connection pool size to reuse connections if you are performing a large amount of concurrent requests.
You can increase the max connection pool size by setting the :code:`max_connection_pool_size` config option.

Debug logging
-------------
If you need to know the details of the http requests the SDK sends under the hood, you can enable debug logging. One way
is to pass :code:`debug=True` argument to :ref:`ClientConfig <class_client_ClientConfig>`. Alternatively, you can toggle debug
logging on and off by setting the :code:`debug` attribute on the :ref:`ClientConfig <class_client_ClientConfig>` object.

.. code:: python
from cognite.client import CogniteClient, ClientConfig
from cognite.client.credentials import Token
client = CogniteClient(
ClientConfig(
client_name="my-client",
project="myproj",
credentials=Token("verysecret"),
debug=True,
)
)
print(client.config.debug) # True, all http request details will be logged
client.config.debug = False # disable debug logging
client.config.debug = True # enable debug logging again
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[tool.poetry]
name = "cognite-sdk"

version = "6.32.2"
version = "6.32.3"
description = "Cognite Python SDK"
readme = "README.md"
documentation = "https://cognite-sdk-python.readthedocs-hosted.com"
Expand Down
2 changes: 1 addition & 1 deletion tests/tests_unit/test_cognite_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_default_client_config_not_set(self) -> None:

def test_client_debug_mode(self):
CogniteClient(ClientConfig(client_name="bla", project="bla", credentials=Token("bla"), debug=True))
log = logging.getLogger("cognite-sdk")
log = logging.getLogger("cognite.client")
assert isinstance(log.handlers[0].formatter, DebugLogFormatter)
log.handlers = []
log.propagate = False
Expand Down
16 changes: 16 additions & 0 deletions tests/tests_unit/test_utils/test_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from cognite.client.utils._logging import (
_configure_logger_for_debug_mode,
_disable_debug_logging,
_is_debug_logging_enabled,
)


class TestDebugLoggingState:
def test_debug_logging_toggle_on_off(self):
assert not _is_debug_logging_enabled(), "should be disabled by default"

_configure_logger_for_debug_mode()
assert _is_debug_logging_enabled(), "should be enabled"

_disable_debug_logging()
assert not _is_debug_logging_enabled(), "should be disabled"

0 comments on commit c54fbc6

Please sign in to comment.