2525#include "exceptions.h"
2626#include "log.h"
2727
28+ bool is_current_log_level_off = true;
29+ PyObject * py_current_custom_callback = NULL ;
30+
2831#ifdef _WIN32
2932 #define __sync_fetch_and_add InterlockedExchangeAdd64
3033#endif
3134
32- static AerospikeLogCallback user_callback ;
33-
34- PyObject * Aerospike_Set_Log_Level (PyObject * parent , PyObject * args ,
35- PyObject * kwds )
36- {
37- // Aerospike vaiables
38- as_error err ;
39- as_status status = AEROSPIKE_OK ;
40-
41- // Python Function Arguments
42- PyObject * py_log_level = NULL ;
43-
44- // Initialise error object.
45- as_error_init (& err );
46-
47- // Python Function Keyword Arguments
48- static char * kwlist [] = {"loglevel" , NULL };
49-
50- // Python Function Argument Parsing
51- if (PyArg_ParseTupleAndKeywords (args , kwds , "O|:setLogLevel" , kwlist ,
52- & py_log_level ) == false) {
53- return NULL ;
54- }
55-
56- // Type check for incoming parameters
57- if (!PyLong_Check (py_log_level )) {
58- as_error_update (& err , AEROSPIKE_ERR_PARAM , "Invalid log level" );
59- goto CLEANUP ;
60- }
61-
62- long lLogLevel = PyLong_AsLong (py_log_level );
63- if (lLogLevel == (uint32_t )-1 && PyErr_Occurred ()) {
64- if (PyErr_ExceptionMatches (PyExc_OverflowError )) {
65- as_error_update (& err , AEROSPIKE_ERR_PARAM ,
66- "integer value exceeds sys.maxsize" );
67- goto CLEANUP ;
68- }
69- }
70-
71- // Invoke C API to set log level
72- as_log_set_level ((as_log_level )lLogLevel );
73-
74- CLEANUP :
75-
76- // Check error object and act accordingly
77- if (err .code != AEROSPIKE_OK ) {
78- raise_exception (& err );
79- return NULL ;
80- }
81-
82- return PyLong_FromLong (status );
83- }
84-
8535volatile int log_counter = 0 ;
8636
87- bool console_log_cb (as_log_level level , const char * func , const char * file ,
88- uint32_t line , const char * fmt , ...)
37+ bool default_log_handler (as_log_level level , const char * func , const char * file ,
38+ uint32_t line , const char * fmt , ...)
8939{
9040
9141 char msg [1024 ];
9242 va_list ap ;
9343
94- int counter = __sync_fetch_and_add (( & log_counter ) , 1 );
44+ int counter = __sync_fetch_and_add (& log_counter , 1 );
9545
9646 va_start (ap , fmt );
9747 vsnprintf (msg , 1024 , fmt , ap );
@@ -102,8 +52,9 @@ bool console_log_cb(as_log_level level, const char *func, const char *file,
10252 return true;
10353}
10454
105- static bool log_cb (as_log_level level , const char * func , const char * file ,
106- uint32_t line , const char * fmt , ...)
55+ static bool call_custom_py_log_handler (as_log_level level , const char * func ,
56+ const char * file , uint32_t line ,
57+ const char * fmt , ...)
10758{
10859
10960 char msg [1024 ];
@@ -112,8 +63,6 @@ static bool log_cb(as_log_level level, const char *func, const char *file,
11263 vsnprintf (msg , 1024 , fmt , ap );
11364 va_end (ap );
11465
115- // Extract pyhton user callback
116- PyObject * py_callback = user_callback .callback ;
11766 // User callback's argument list
11867 PyObject * py_arglist = NULL ;
11968
@@ -139,7 +88,7 @@ static bool log_cb(as_log_level level, const char *func, const char *file,
13988 PyTuple_SetItem (py_arglist , 4 , message );
14089
14190 // Invoke user callback, passing in argument's list
142- PyObject_Call (py_callback , py_arglist , NULL );
91+ PyObject_Call (py_current_custom_callback , py_arglist , NULL );
14392
14493 Py_DECREF (py_arglist );
14594
@@ -149,31 +98,100 @@ static bool log_cb(as_log_level level, const char *func, const char *file,
14998 return true;
15099}
151100
101+ PyObject * Aerospike_Set_Log_Level (PyObject * parent , PyObject * args ,
102+ PyObject * kwds )
103+ {
104+ as_status status = AEROSPIKE_OK ;
105+
106+ // Python Function Argument Parsing
107+ static char * kwlist [] = {"loglevel" , NULL };
108+ PyObject * py_log_level = NULL ;
109+
110+ if (PyArg_ParseTupleAndKeywords (args , kwds , "O|:setLogLevel" , kwlist ,
111+ & py_log_level ) == false) {
112+ return NULL ;
113+ }
114+
115+ as_error err ;
116+ as_error_init (& err );
117+
118+ // Type check for incoming parameters
119+ if (!PyLong_Check (py_log_level )) {
120+ as_error_update (& err , AEROSPIKE_ERR_PARAM , "Invalid log level" );
121+ goto CLEANUP ;
122+ }
123+
124+ long log_level = PyLong_AsLong (py_log_level );
125+ if (log_level == -1 && PyErr_Occurred ()) {
126+ if (PyErr_ExceptionMatches (PyExc_OverflowError )) {
127+ as_error_update (& err , AEROSPIKE_ERR_PARAM ,
128+ "integer value exceeds sys.maxsize" );
129+ goto CLEANUP ;
130+ }
131+ }
132+
133+ is_current_log_level_off = log_level == LOG_LEVEL_OFF ;
134+
135+ if (log_level == LOG_LEVEL_OFF ) {
136+ as_log_set_callback (NULL );
137+ }
138+ else {
139+ as_log_set_level ((as_log_level )log_level );
140+
141+ // Re-enable log handler
142+ if (py_current_custom_callback != NULL ) {
143+ as_log_set_callback ((as_log_callback )call_custom_py_log_handler );
144+ }
145+ else {
146+ as_log_set_callback ((as_log_callback )default_log_handler );
147+ }
148+ }
149+
150+ CLEANUP :
151+
152+ if (err .code != AEROSPIKE_OK ) {
153+ raise_exception (& err );
154+ return NULL ;
155+ }
156+
157+ return PyLong_FromLong (status );
158+ }
159+
152160PyObject * Aerospike_Set_Log_Handler (PyObject * parent , PyObject * args ,
153161 PyObject * kwds )
154162{
155163 // Python variables
156164 PyObject * py_callback = NULL ;
157- as_error err ;
158- as_error_init (& err );
159165 // Python function keyword arguments
160166 static char * kwlist [] = {"log_handler" , NULL };
161167
162168 // Python function arguments parsing
163- PyArg_ParseTupleAndKeywords (args , kwds , "|O:setLogHandler" , kwlist ,
164- & py_callback );
169+ if (PyArg_ParseTupleAndKeywords (args , kwds , "|O:setLogHandler" , kwlist ,
170+ & py_callback ) == false) {
171+ return NULL ;
172+ }
165173
166- if (py_callback && PyCallable_Check (py_callback )) {
167- // Store user callback
168- Py_INCREF (py_callback );
169- user_callback .callback = py_callback ;
174+ // Clean up existing log handler
175+ Py_CLEAR (py_current_custom_callback );
170176
171- // Register callback to C-SDK
172- as_log_set_callback ((as_log_callback )log_cb );
177+ // 3 cases (when args are passed):
178+ if (py_callback == NULL ) {
179+ // 1. No args -> enable Python client's default log handler IF log level is not OFF
180+ // IF log level is OFF, don't enable the default log handler.
181+ if (!is_current_log_level_off ) {
182+ as_log_set_callback ((as_log_callback )default_log_handler );
183+ }
173184 }
174- else {
175- // Register callback to C-SDK
176- as_log_set_callback ((as_log_callback )console_log_cb );
185+ else if (Py_IsNone (py_callback )) {
186+ // Disable log handler altogether
187+ as_log_set_callback (NULL );
188+ }
189+ else if (PyCallable_Check (py_callback )) {
190+ // Register custom log handler
191+ py_current_custom_callback = Py_NewRef (py_callback );
192+ if (!is_current_log_level_off ) {
193+ as_log_set_callback ((as_log_callback )call_custom_py_log_handler );
194+ }
177195 }
178196
179197 return PyLong_FromLong (0 );
@@ -184,7 +202,7 @@ void Aerospike_Enable_Default_Logging()
184202 // Invoke C API to set log level
185203 as_log_set_level ((as_log_level )AS_LOG_LEVEL_ERROR );
186204 // Register callback to C-SDK
187- as_log_set_callback ((as_log_callback )console_log_cb );
205+ as_log_set_callback ((as_log_callback )default_log_handler );
188206
189207 return ;
190208}
0 commit comments