Skip to content

Commit 846a9ca

Browse files
committed
PYCBC-1619: Enable use of C++ file logger
Motivation ========== Allow users to output C++ core logging to file. Modification ============ * IF PYCBC_LOG_FILE environment variable is set, use C++ core file logger. NOTE: PYCBC_LOG_LEVEL must be set as well. Results ======= * C++ core logging outputs to file when PYCBC_LOG_LEVEL and PYCBC_LOG_FILE are set. Change-Id: I59029bc36b233f8b783d97f3832b9e4e3d839ed1 Reviewed-on: https://review.couchbase.org/c/couchbase-python-client/+/217909 Reviewed-by: Michael Reiche <michael.reiche@couchbase.com> Tested-by: Build Bot <build@couchbase.com> Reviewed-by: Sergey Avseyev <sergey.avseyev@gmail.com>
1 parent 0a10904 commit 846a9ca

File tree

3 files changed

+69
-31
lines changed

3 files changed

+69
-31
lines changed

couchbase/__init__.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,11 @@
8484
def _pycbc_teardown(**kwargs):
8585
"""**INTERNAL**"""
8686
global _PYCBC_LOGGER
87-
# if using a console logger we let the nature course of shutdown happening, if using Python logging
87+
# if using a console logger we let the natural course of shutdown happen, if using Python logging
8888
# we need a cleaner mechanism to shutdown the C++ logger prior to the Python interpreter starting to finalize
89-
if _PYCBC_LOGGER and isinstance(_PYCBC_LOGGER, pycbc_logger) and not _PYCBC_LOGGER.is_console_logger():
89+
if (_PYCBC_LOGGER
90+
and isinstance(_PYCBC_LOGGER, pycbc_logger)
91+
and not (_PYCBC_LOGGER.is_console_logger() or _PYCBC_LOGGER.is_file_logger())):
9092
shutdown_logger()
9193
_PYCBC_LOGGER = None
9294

@@ -143,14 +145,22 @@ def configure_console_logger():
143145
import os
144146
log_level = os.getenv('PYCBC_LOG_LEVEL', None)
145147
if log_level:
146-
_PYCBC_LOGGER.create_console_logger(log_level.lower())
148+
log_file = os.getenv('PYCBC_LOG_FILE', None)
149+
if log_file:
150+
_PYCBC_LOGGER.create_logger(level=log_level.lower(), filename=log_file)
151+
else:
152+
_PYCBC_LOGGER.create_logger(level=log_level.lower())
147153
logging.getLogger().debug(get_metadata(as_str=True))
148154

149155

150156
def configure_logging(name, level=logging.INFO, parent_logger=None):
151157
if parent_logger:
152158
name = f'{parent_logger.name}.{name}'
153159
logger = logging.getLogger(name)
160+
if _PYCBC_LOGGER.is_console_logger() or _PYCBC_LOGGER.is_file_logger():
161+
raise RuntimeError(('Cannot create logger. Another logger has already been '
162+
'initialized. Make sure the PYCBC_LOG_LEVEL and PYCBC_LOG_FILE env '
163+
'variable are not set if using configure_logging.'))
154164
_PYCBC_LOGGER.configure_logging_sink(logger, level)
155165
logger.debug(get_metadata(as_str=True))
156166

src/logger.cxx

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,19 @@ pycbc_logger__configure_logging_sink__(PyObject* self, PyObject* args, PyObject*
6666
}
6767

6868
PyObject*
69-
pycbc_logger__create_console_logger__(PyObject* self, PyObject* args, PyObject* kwargs)
69+
pycbc_logger__create_logger__(PyObject* self, PyObject* args, PyObject* kwargs)
7070
{
7171
auto logger = reinterpret_cast<pycbc_logger*>(self);
7272
char* log_level = nullptr;
73-
const char* kw_list[] = { "level", nullptr };
74-
const char* kw_format = "s";
73+
char* log_filename = nullptr;
74+
const char* kw_list[] = { "level", "filename", nullptr };
75+
const char* kw_format = "s|s";
7576
if (!PyArg_ParseTupleAndKeywords(
76-
args, kwargs, kw_format, const_cast<char**>(kw_list), &log_level)) {
77+
args, kwargs, kw_format, const_cast<char**>(kw_list), &log_level, &log_filename)) {
7778
pycbc_set_python_exception(PycbcError::InvalidArgument,
7879
__FILE__,
7980
__LINE__,
80-
"Cannot set create console logger. Unable to parse args/kwargs.");
81+
"Cannot create logger. Unable to parse args/kwargs.");
8182
return nullptr;
8283
}
8384

@@ -86,21 +87,29 @@ pycbc_logger__create_console_logger__(PyObject* self, PyObject* args, PyObject*
8687
PycbcError::UnsuccessfulOperation,
8788
__FILE__,
8889
__LINE__,
89-
"Cannot create console logger. Another logger has already been initialized.");
90+
"Cannot create logger. Another logger has already been initialized.");
9091
return nullptr;
9192
}
9293

9394
if (log_level == nullptr) {
9495
pycbc_set_python_exception(PycbcError::InvalidArgument,
9596
__FILE__,
9697
__LINE__,
97-
"Cannot create console logger. Unable to determine log level.");
98+
"Cannot create logger. Unable to determine log level.");
9899
return nullptr;
99100
}
100-
couchbase::core::logger::create_console_logger();
101101
auto level = couchbase::core::logger::level_from_str(log_level);
102-
couchbase::core::logger::set_log_levels(level);
103-
logger->is_console_logger = true;
102+
if (log_filename != nullptr) {
103+
couchbase::core::logger::configuration configuration{};
104+
configuration.filename = std::string{ log_filename };
105+
configuration.log_level = level;
106+
couchbase::core::logger::create_file_logger(configuration);
107+
logger->is_file_logger = true;
108+
} else {
109+
couchbase::core::logger::create_console_logger();
110+
couchbase::core::logger::set_log_levels(level);
111+
logger->is_console_logger = true;
112+
}
104113
Py_RETURN_NONE;
105114
}
106115

@@ -137,24 +146,42 @@ pycbc_logger__is_console_logger__(PyObject* self, PyObject* Py_UNUSED(ignored))
137146
}
138147
}
139148

140-
static PyMethodDef pycbc_logger_methods[] = { { "configure_logging_sink",
141-
(PyCFunction)pycbc_logger__configure_logging_sink__,
142-
METH_VARARGS | METH_KEYWORDS,
143-
PyDoc_STR("Configure logger's logging sink") },
144-
{ "create_console_logger",
145-
(PyCFunction)pycbc_logger__create_console_logger__,
146-
METH_VARARGS | METH_KEYWORDS,
147-
PyDoc_STR("Create a console logger") },
148-
{ "enable_protocol_logger",
149-
(PyCFunction)pycbc_logger__enable_protocol_logger__,
150-
METH_VARARGS | METH_KEYWORDS,
151-
PyDoc_STR("Enables the protocol logger") },
152-
{ "is_console_logger",
153-
(PyCFunction)pycbc_logger__is_console_logger__,
154-
METH_NOARGS,
155-
PyDoc_STR(
156-
"Check if logger is console logger or not") },
157-
{ NULL } };
149+
PyObject*
150+
pycbc_logger__is_file_logger__(PyObject* self, PyObject* Py_UNUSED(ignored))
151+
{
152+
auto logger = reinterpret_cast<pycbc_logger*>(self);
153+
if (logger->is_file_logger) {
154+
Py_INCREF(Py_True);
155+
return Py_True;
156+
} else {
157+
Py_INCREF(Py_False);
158+
return Py_False;
159+
}
160+
}
161+
162+
static PyMethodDef pycbc_logger_methods[] = {
163+
{ "configure_logging_sink",
164+
(PyCFunction)pycbc_logger__configure_logging_sink__,
165+
METH_VARARGS | METH_KEYWORDS,
166+
PyDoc_STR("Configure logger's logging sink") },
167+
{ "create_logger",
168+
(PyCFunction)pycbc_logger__create_logger__,
169+
METH_VARARGS | METH_KEYWORDS,
170+
PyDoc_STR("Create a C++ core logger") },
171+
{ "enable_protocol_logger",
172+
(PyCFunction)pycbc_logger__enable_protocol_logger__,
173+
METH_VARARGS | METH_KEYWORDS,
174+
PyDoc_STR("Enables the protocol logger") },
175+
{ "is_console_logger",
176+
(PyCFunction)pycbc_logger__is_console_logger__,
177+
METH_NOARGS,
178+
PyDoc_STR("Check if logger is console logger or not") },
179+
{ "is_file_logger",
180+
(PyCFunction)pycbc_logger__is_file_logger__,
181+
METH_NOARGS,
182+
PyDoc_STR("Check if logger is file logger or not") },
183+
{ NULL }
184+
};
158185

159186
static PyObject*
160187
pycbc_logger_new(PyTypeObject* type, PyObject*, PyObject*)

src/logger.hxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ private:
225225
struct pycbc_logger {
226226
PyObject_HEAD std::shared_ptr<pycbc_logger_sink> logger_sink_;
227227
bool is_console_logger{ false };
228+
bool is_file_logger{ false };
228229
};
229230

230231
PyObject*

0 commit comments

Comments
 (0)