From 970765372717ce052a85c06d2d626f466ffaa0b6 Mon Sep 17 00:00:00 2001 From: Sven Trenkel Date: Wed, 5 Jul 2017 21:24:56 +0000 Subject: [PATCH] Add CollectdError exception which can be thrown without causing a stacktrace to be logged. --- src/cpython.h | 4 +-- src/python.c | 85 +++++++++++++++++++++++++++++++++++--------------- src/pyvalues.c | 4 +-- 3 files changed, 63 insertions(+), 30 deletions(-) diff --git a/src/cpython.h b/src/cpython.h index 41170404..5389e7e8 100644 --- a/src/cpython.h +++ b/src/cpython.h @@ -145,7 +145,7 @@ void cpy_log_exception(const char *context); typedef struct { PyObject_HEAD /* No semicolon! */ - PyObject *parent; /* Config */ + PyObject *parent; /* Config */ PyObject *key; /* String */ PyObject *values; /* Sequence */ PyObject *children; /* Sequence */ @@ -154,7 +154,7 @@ extern PyTypeObject ConfigType; typedef struct { PyObject_HEAD /* No semicolon! */ - double time; + double time; char host[DATA_MAX_NAME_LEN]; char plugin[DATA_MAX_NAME_LEN]; char plugin_instance[DATA_MAX_NAME_LEN]; diff --git a/src/python.c b/src/python.c index b106e45c..4807ba7f 100644 --- a/src/python.c +++ b/src/python.c @@ -233,6 +233,12 @@ static char reg_shutdown_doc[] = "The callback function will be called with no parameters except for\n" " data if it was supplied."; +static char CollectdError_doc[] = + "Basic exception for collectd Python scripts.\n" + "\n" + "Throwing this exception will not cause a stacktrace to be logged, \n" + "even if LogTraces is enabled in the config."; + static pthread_t main_thread; static PyOS_sighandler_t python_sigint_handler; static _Bool do_interactive = 0; @@ -244,7 +250,7 @@ static _Bool do_interactive = 0; static PyThreadState *state; -static PyObject *sys_path, *cpy_format_exception; +static PyObject *sys_path, *cpy_format_exception, *CollectdError; static cpy_callback_t *cpy_config_callbacks; static cpy_callback_t *cpy_init_callbacks; @@ -300,7 +306,7 @@ static void cpy_build_name(char *buf, size_t size, PyObject *callback, } void cpy_log_exception(const char *context) { - int l = 0; + int l = 0, collectd_error; const char *typename = NULL, *message = NULL; PyObject *type, *value, *traceback, *tn, *m, *list; @@ -308,6 +314,7 @@ void cpy_log_exception(const char *context) { PyErr_NormalizeException(&type, &value, &traceback); if (type == NULL) return; + collectd_error = PyErr_GivenExceptionMatches(value, CollectdError); tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */ m = PyObject_Str(value); /* New reference. */ if (tn != NULL) @@ -318,11 +325,17 @@ void cpy_log_exception(const char *context) { typename = "NamelessException"; if (message == NULL) message = "N/A"; - Py_BEGIN_ALLOW_THREADS ERROR("Unhandled python exception in %s: %s: %s", - context, typename, message); - Py_END_ALLOW_THREADS Py_XDECREF(tn); + Py_BEGIN_ALLOW_THREADS + if (collectd_error) { + WARNING("%s in %s: %s", typename, context, message); + } else { + ERROR("Unhandled python exception in %s: %s: %s", + context, typename, message); + } + Py_END_ALLOW_THREADS + Py_XDECREF(tn); Py_XDECREF(m); - if (!cpy_format_exception || !traceback) { + if (!cpy_format_exception || !traceback || collectd_error) { PyErr_Clear(); Py_DECREF(type); Py_XDECREF(value); @@ -356,10 +369,11 @@ void cpy_log_exception(const char *context) { if (cpy[strlen(cpy) - 1] == '\n') cpy[strlen(cpy) - 1] = 0; - Py_BEGIN_ALLOW_THREADS ERROR("%s", cpy); + Py_BEGIN_ALLOW_THREADS + ERROR("%s", cpy); Py_END_ALLOW_THREADS - free(cpy); + free(cpy); } Py_XDECREF(list); @@ -410,9 +424,10 @@ static int cpy_write_callback(const data_set_t *ds, PyList_SetItem( list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute)); } else { - Py_BEGIN_ALLOW_THREADS ERROR("cpy_write_callback: Unknown value type %d.", - ds->ds[i].type); - Py_END_ALLOW_THREADS Py_DECREF(list); + Py_BEGIN_ALLOW_THREADS + ERROR("cpy_write_callback: Unknown value type %d.", ds->ds[i].type); + Py_END_ALLOW_THREADS + Py_DECREF(list); CPY_RETURN_FROM_THREADS 0; } if (PyErr_Occurred() != NULL) { @@ -668,8 +683,10 @@ static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) { if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin, &timeout, NULL, &identifier) == 0) return NULL; - Py_BEGIN_ALLOW_THREADS plugin_flush(plugin, timeout, identifier); - Py_END_ALLOW_THREADS PyMem_Free(plugin); + Py_BEGIN_ALLOW_THREADS + plugin_flush(plugin, timeout, identifier); + Py_END_ALLOW_THREADS + PyMem_Free(plugin); PyMem_Free(identifier); Py_RETURN_NONE; } @@ -803,8 +820,10 @@ static PyObject *cpy_error(PyObject *self, PyObject *args) { char *text; if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL; - Py_BEGIN_ALLOW_THREADS plugin_log(LOG_ERR, "%s", text); - Py_END_ALLOW_THREADS PyMem_Free(text); + Py_BEGIN_ALLOW_THREADS + plugin_log(LOG_ERR, "%s", text); + Py_END_ALLOW_THREADS + PyMem_Free(text); Py_RETURN_NONE; } @@ -812,8 +831,10 @@ static PyObject *cpy_warning(PyObject *self, PyObject *args) { char *text; if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL; - Py_BEGIN_ALLOW_THREADS plugin_log(LOG_WARNING, "%s", text); - Py_END_ALLOW_THREADS PyMem_Free(text); + Py_BEGIN_ALLOW_THREADS + plugin_log(LOG_WARNING, "%s", text); + Py_END_ALLOW_THREADS + PyMem_Free(text); Py_RETURN_NONE; } @@ -821,8 +842,10 @@ static PyObject *cpy_notice(PyObject *self, PyObject *args) { char *text; if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL; - Py_BEGIN_ALLOW_THREADS plugin_log(LOG_NOTICE, "%s", text); - Py_END_ALLOW_THREADS PyMem_Free(text); + Py_BEGIN_ALLOW_THREADS + plugin_log(LOG_NOTICE, "%s", text); + Py_END_ALLOW_THREADS + PyMem_Free(text); Py_RETURN_NONE; } @@ -830,8 +853,10 @@ static PyObject *cpy_info(PyObject *self, PyObject *args) { char *text; if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL; - Py_BEGIN_ALLOW_THREADS plugin_log(LOG_INFO, "%s", text); - Py_END_ALLOW_THREADS PyMem_Free(text); + Py_BEGIN_ALLOW_THREADS + plugin_log(LOG_INFO, "%s", text); + Py_END_ALLOW_THREADS + PyMem_Free(text); Py_RETURN_NONE; } @@ -840,8 +865,10 @@ static PyObject *cpy_debug(PyObject *self, PyObject *args) { char *text; if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL; - Py_BEGIN_ALLOW_THREADS plugin_log(LOG_DEBUG, "%s", text); - Py_END_ALLOW_THREADS PyMem_Free(text); + Py_BEGIN_ALLOW_THREADS + plugin_log(LOG_DEBUG, "%s", text); + Py_END_ALLOW_THREADS + PyMem_Free(text); #endif Py_RETURN_NONE; } @@ -1025,13 +1052,14 @@ static int cpy_shutdown(void) { } PyErr_Print(); - Py_BEGIN_ALLOW_THREADS cpy_unregister_list(&cpy_config_callbacks); + Py_BEGIN_ALLOW_THREADS + cpy_unregister_list(&cpy_config_callbacks); cpy_unregister_list(&cpy_init_callbacks); cpy_unregister_list(&cpy_shutdown_callbacks); cpy_shutdown_triggered = 1; Py_END_ALLOW_THREADS - if (!cpy_num_callbacks) { + if (!cpy_num_callbacks) { Py_Finalize(); return 0; } @@ -1173,7 +1201,7 @@ PyMODINIT_FUNC PyInit_collectd(void) { static int cpy_init_python(void) { PyOS_sighandler_t cur_sig; - PyObject *sys; + PyObject *sys, *errordict; PyObject *module; #ifdef IS_PY3K @@ -1200,6 +1228,9 @@ static int cpy_init_python(void) { PyType_Ready(&SignedType); UnsignedType.tp_base = &PyLong_Type; PyType_Ready(&UnsignedType); + errordict = PyDict_New(); + PyDict_SetItemString(errordict, "__doc__", cpy_string_to_unicode_or_bytes(CollectdError_doc)); /* New reference. */ + CollectdError = PyErr_NewException("collectd.CollectdError", NULL, errordict); sys = PyImport_ImportModule("sys"); /* New reference. */ if (sys == NULL) { cpy_log_exception("python initialization"); @@ -1229,6 +1260,8 @@ static int cpy_init_python(void) { (void *)&SignedType); /* Steals a reference. */ PyModule_AddObject(module, "Unsigned", (void *)&UnsignedType); /* Steals a reference. */ + Py_XINCREF(CollectdError); + PyModule_AddObject(module, "CollectdError", CollectdError); /* Steals a reference. */ PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG); PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO); PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE); diff --git a/src/pyvalues.c b/src/pyvalues.c index e1856b84..4f7e8349 100644 --- a/src/pyvalues.c +++ b/src/pyvalues.c @@ -1175,7 +1175,7 @@ PyTypeObject SignedType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ Signed_doc /* tp_doc */ }; @@ -1202,6 +1202,6 @@ PyTypeObject UnsignedType = { 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ Unsigned_doc /* tp_doc */ }; -- 2.30.2