Skip to content

Commit d37b524

Browse files
committed
PYCBC-1754: Logging Improvements
Motivation ========== With the addition of threshold logging and meter logging now being in the Python SDK (previously handled by the underlying C++ SDK), we want to make sure the SDK integrates into the Python logging ecosystem correctly. Modification ============ * Moved some logic out of couchbase/__init__.py to specific modules (logic/logging_config.py & logic/pycbc_core/core_metadata.py) * Added quick smoke tests to confirm logging imports are handled correctly * Added LOGGING.md and examples/logging/ to provide more insight and details into logging w/ the Python SDK. Change-Id: If681e8e0248573df5b3f14ab9aac43ae72179d17 Reviewed-on: https://review.couchbase.org/c/couchbase-python-client/+/242146 Tested-by: Build Bot <build@couchbase.com> Reviewed-by: Dimitris Christodoulou <dimitris.christodoulou@couchbase.com>
1 parent 558dd20 commit d37b524

12 files changed

Lines changed: 2245 additions & 130 deletions

LOGGING.md

Lines changed: 751 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Python client for [Couchbase](https://couchbase.com)
88
- [Installing](#installing)
99
- [Building](#building)
1010
- [Using the SDK](#using-the-sdk)
11+
- [Logging](#logging)
1112
- [Building Documentation](#building-documentation)
1213
- [Testing](#testing)
1314
- [Contributing](#contributing)
@@ -450,6 +451,36 @@ d.addCallback(get_document)
450451

451452
reactor.run()
452453
```
454+
455+
# Logging<a id="logging"></a>
456+
[Back to Contents](#contents)
457+
458+
The Couchbase Python SDK provides comprehensive logging for debugging and monitoring.
459+
460+
## Quick Start
461+
462+
Enable logging via environment variable:
463+
```console
464+
$ export PYCBC_LOG_LEVEL=info
465+
$ python your_app.py
466+
```
467+
468+
Or configure programmatically:
469+
```python
470+
import logging
471+
from couchbase import configure_logging
472+
473+
configure_logging('my_app', level=logging.DEBUG)
474+
```
475+
476+
## Documentation
477+
478+
For complete logging documentation including configuration options, examples,
479+
troubleshooting, and best practices, see **[LOGGING.md](LOGGING.md)**.
480+
481+
For working example scripts demonstrating various logging configurations, see
482+
**[examples/logging/](examples/logging/)**.
483+
453484
# Building Documentation<a id="building-documentation"></a>
454485
[Back to Contents](#contents)
455486

couchbase/__init__.py

Lines changed: 12 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2016-2022. Couchbase, Inc.
1+
# Copyright 2016-2026. Couchbase, Inc.
22
# All Rights Reserved.
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License")
@@ -14,10 +14,6 @@
1414
# limitations under the License.
1515

1616
import platform
17-
from functools import partial, partialmethod
18-
from typing import (List,
19-
Optional,
20-
Tuple)
2117

2218
try:
2319
# Importing the ssl package allows us to utilize some Python voodoo to find OpenSSL.
@@ -58,130 +54,16 @@
5854
pass
5955

6056

61-
""" Add support for logging, adding a TRACE level to logging """
62-
import json # nopep8 # isort:skip # noqa: E402
63-
import logging # nopep8 # isort:skip # noqa: E402
57+
# Import logging configuration - auto-configures on import
58+
import couchbase.logic.logging_config # nopep8 # isort:skip # noqa: F401, E402
6459

65-
from couchbase.logic.pycbc_core import CXXCBC_METADATA, pycbc_logger, shutdown_logger # nopep8 # isort:skip # noqa: E402, E501
60+
# Import public API functions
61+
from couchbase.logic.logging_config import ( # nopep8 # isort:skip # noqa: F401, E402
62+
configure_logging,
63+
enable_protocol_logger_to_save_network_traffic_to_file,
64+
)
6665

67-
_PYCBC_LOGGER = pycbc_logger()
68-
_CXXCBC_METADATA_JSON = json.loads(CXXCBC_METADATA)
69-
logging.TRACE = 5
70-
logging.addLevelName(logging.TRACE, 'TRACE')
71-
logging.Logger.trace = partialmethod(logging.Logger.log, logging.TRACE)
72-
logging.trace = partial(logging.log, logging.TRACE)
73-
74-
75-
"""
76-
77-
pycbc teardown methods
78-
79-
"""
80-
import atexit # nopep8 # isort:skip # noqa: E402
81-
82-
83-
def _pycbc_teardown(**kwargs):
84-
"""**INTERNAL**"""
85-
global _PYCBC_LOGGER
86-
# if using a console logger we let the natural course of shutdown happen, if using Python logging
87-
# we need a cleaner mechanism to shutdown the C++ logger prior to the Python interpreter starting to finalize
88-
if (_PYCBC_LOGGER
89-
and isinstance(_PYCBC_LOGGER, pycbc_logger)
90-
and not (_PYCBC_LOGGER.is_console_logger() or _PYCBC_LOGGER.is_file_logger())):
91-
shutdown_logger()
92-
_PYCBC_LOGGER = None
93-
94-
95-
atexit.register(_pycbc_teardown)
96-
97-
"""
98-
99-
Metadata + version methods
100-
101-
"""
102-
_METADATA_KEYS = ['openssl_default_cert_dir',
103-
'openssl_default_cert_file',
104-
'openssl_headers',
105-
'openssl_runtime',
106-
'txns_forward_compat_extensions',
107-
'txns_forward_compat_protocol_version',
108-
'version']
109-
110-
111-
def get_metadata(as_str=False, detailed=False):
112-
metadata = _CXXCBC_METADATA_JSON if detailed is True else {
113-
k: v for k, v in _CXXCBC_METADATA_JSON.items() if k in _METADATA_KEYS}
114-
return json.dumps(metadata) if as_str is True else metadata
115-
116-
117-
def get_transactions_protocol() -> Optional[Tuple[Optional[float], Optional[List[str]]]]:
118-
"""Get the transactions protocol version and supported extensions.
119-
120-
Returns:
121-
Optional[Tuple[Optional[float], Optional[List[str]]]]: The transactions protocol version and
122-
support extensions, if found in the cxx client's metadata.
123-
"""
124-
if not _CXXCBC_METADATA_JSON:
125-
return None
126-
127-
version = _CXXCBC_METADATA_JSON.get('txns_forward_compat_protocol_version', None)
128-
if version:
129-
version = float(version)
130-
extensions = _CXXCBC_METADATA_JSON.get('txns_forward_compat_extensions', None)
131-
if extensions:
132-
extensions = extensions.split(',')
133-
return version, extensions
134-
135-
136-
"""
137-
138-
Logging methods
139-
140-
"""
141-
142-
143-
def configure_console_logger():
144-
import os
145-
log_level = os.getenv('PYCBC_LOG_LEVEL', None)
146-
if log_level:
147-
log_file = os.getenv('PYCBC_LOG_FILE', None)
148-
if log_file:
149-
enable_console_logging = 0 if os.getenv('PYCBC_ENABLE_CONSOLE', None) is None else 1
150-
_PYCBC_LOGGER.create_logger(level=log_level.lower(),
151-
filename=log_file,
152-
enable_console=enable_console_logging)
153-
else:
154-
_PYCBC_LOGGER.create_logger(level=log_level.lower())
155-
logging.getLogger().debug(get_metadata(as_str=True))
156-
157-
158-
def configure_logging(name, level=logging.INFO, parent_logger=None):
159-
if parent_logger:
160-
name = f'{parent_logger.name}.{name}'
161-
logger = logging.getLogger(name)
162-
if _PYCBC_LOGGER.is_console_logger() or _PYCBC_LOGGER.is_file_logger():
163-
raise RuntimeError(('Cannot create logger. Another logger has already been '
164-
'initialized. Make sure the PYCBC_LOG_LEVEL and PYCBC_LOG_FILE env '
165-
'variable are not set if using configure_logging.'))
166-
_PYCBC_LOGGER.configure_logging_sink(logger, level)
167-
logger.debug(get_metadata(as_str=True))
168-
169-
170-
def enable_protocol_logger_to_save_network_traffic_to_file(filename # type: str
171-
):
172-
"""
173-
**VOLATILE** This API is subject to change at any time.
174-
175-
Exposes the underlying couchbase++ library protocol logger. This method is for logging/debugging
176-
purposes and must be used with caution as network details will be logged to the provided file.
177-
178-
Args:
179-
filename (str): The name of the file the protocol logger will write to.
180-
181-
Raises:
182-
InvalidArgumentException: If a filename is not provided.
183-
"""
184-
_PYCBC_LOGGER.enable_protocol_logger(filename)
185-
186-
187-
configure_console_logger()
66+
from couchbase.logic.pycbc_core.core_metadata import ( # nopep8 # isort:skip # noqa: F401, E402
67+
get_metadata,
68+
get_transactions_protocol,
69+
)

0 commit comments

Comments
 (0)