[PATCH 12/14] pytdb: Add support for logging
Kirill Smelkov
kirr at landau.phys.spbu.ru
Sat Oct 2 07:43:51 MDT 2010
Logging is important when doing crash recovery and checks, and also
since it is used in tdbtorture, let's add support for it.
NOTE: when setting log_fn in Tdb ctor - it is not hooked immediately
like in C tdb_open_ex(). The point here is that there is no python
object yet, at the stage when tdb_open* is called.
Signed-off-by: Kirill Smelkov <kirr at landau.phys.spbu.ru>
---
lib/tdb/pytdb.c | 91 ++++++++++++++++++++++++++++++++++++++-
lib/tdb/python/tests/simple.py | 17 +++++++
2 files changed, 105 insertions(+), 3 deletions(-)
diff --git a/lib/tdb/pytdb.c b/lib/tdb/pytdb.c
index 713b026..6b66fd3 100644
--- a/lib/tdb/pytdb.c
+++ b/lib/tdb/pytdb.c
@@ -38,6 +38,7 @@
typedef struct {
PyObject_HEAD
TDB_CONTEXT *ctx;
+ PyObject *py_logfn;
bool closed;
} PyTdbObject;
@@ -100,21 +101,27 @@ static PyObject *PyString_FromTDB_DATA(TDB_DATA data)
Py_RETURN_TRUE; \
} while (0)
+static void __obj_set_logging_function(PyTdbObject *self, PyObject *py_logfn);
+
+
static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
char *name = NULL;
int hash_size = 0, tdb_flags = TDB_DEFAULT, flags = O_RDWR, mode = 0600;
+ PyObject *py_logfn = Py_None;
TDB_CONTEXT *ctx;
PyTdbObject *ret;
- const char *kwnames[] = { "name", "hash_size", "tdb_flags", "flags", "mode", NULL };
+ const char *kwnames[] = { "name", "hash_size", "tdb_flags", "flags", "mode", "log_fn", NULL };
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiii", (char **)kwnames, &name, &hash_size, &tdb_flags, &flags, &mode))
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiiiO", (char **)kwnames, &name, &hash_size, &tdb_flags, &flags, &mode, &py_logfn))
return NULL;
if (name == NULL) {
tdb_flags |= TDB_INTERNAL;
}
+ /* we can't init logging at this stage because python tdb object does
+ * not exist yet */
ctx = tdb_open(name, hash_size, tdb_flags, flags, mode);
if (ctx == NULL) {
PyErr_SetFromErrno(PyExc_IOError);
@@ -129,9 +136,76 @@ static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwarg
ret->ctx = ctx;
ret->closed = false;
+ ret->py_logfn = Py_None; Py_INCREF(Py_None);
+ __obj_set_logging_function(ret, py_logfn);
return (PyObject *)ret;
}
+/* logging function for log_fn=None */
+static void py_logfn_none(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
+{
+}
+
+static void py_logfn_helper(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
+{
+ va_list ap;
+ char *msg;
+ int len;
+ PyObject *pyret;
+ PyTdbObject *self = tdb_get_logging_private(tdb);
+
+ va_start(ap, format);
+ len = vasprintf(&msg, format, ap);
+ va_end(ap);
+ if (len < 0) {
+ msg = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "tdb.py_logfn_helper: vasprintf failed");
+ goto out;
+ }
+
+ /* logfn(db, level, msg) */
+ pyret = PyObject_CallFunction(self->py_logfn, "Ois", self, (int)level, msg);
+ Py_XDECREF(pyret);
+
+out:
+ /* we are in void-return callback from tdb code - we can't raise */
+ if (PyErr_Occurred())
+ PyErr_Print();
+
+ free(msg);
+}
+
+static void __obj_set_logging_function(PyTdbObject *self, PyObject *py_logfn)
+{
+ struct tdb_logging_context log_ctx;
+
+ Py_INCREF(py_logfn);
+ Py_DECREF(self->py_logfn);
+ self->py_logfn = py_logfn;
+
+ log_ctx.log_fn = (py_logfn != Py_None ? py_logfn_helper : py_logfn_none);
+ log_ctx.log_private = self;
+
+ tdb_set_logging_function(self->ctx, &log_ctx);
+}
+
+static PyObject *obj_set_logging_function(PyTdbObject *self, PyObject *args)
+{
+ PyObject *py_logfn;
+ if (!PyArg_ParseTuple(args, "O", &py_logfn))
+ return NULL;
+
+ __obj_set_logging_function(self, py_logfn);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_log_fn(PyTdbObject *self)
+{
+ Py_INCREF(self->py_logfn);
+ return self->py_logfn;
+}
+
static PyObject *obj_transaction_cancel(PyTdbObject *self)
{
int ret = tdb_transaction_cancel(self->ctx);
@@ -669,6 +743,11 @@ static PyMethodDef tdb_object_methods[] = {
{ "traverse_read", (PyCFunction)obj_traverse_read, METH_VARARGS,
"S.traverse_read(fn|None) -> ntraversed\n"
"fn(key, data) -> continue?" },
+ { "set_logging_function", (PyCFunction)obj_set_logging_function, METH_VARARGS,
+ "S.set_logging_function(logfn|None) -> None\n"
+ "logfn(db, level, msg)" },
+ { "log_fn", (PyCFunction)obj_log_fn, METH_NOARGS,
+ "S.log_fn() -> logfn | None" },
{ NULL }
};
@@ -735,6 +814,7 @@ static void tdb_object_dealloc(PyTdbObject *self)
{
if (!self->closed)
tdb_close(self->ctx);
+ Py_DECREF(self->py_logfn);
PyObject_Del(self);
}
@@ -809,7 +889,7 @@ PyTypeObject PyTdb = {
};
static PyMethodDef tdb_methods[] = {
- { "open", (PyCFunction)py_tdb_open, METH_VARARGS|METH_KEYWORDS, "open(name, hash_size=0, tdb_flags=TDB_DEFAULT, flags=O_RDWR, mode=0600)\n"
+ { "open", (PyCFunction)py_tdb_open, METH_VARARGS|METH_KEYWORDS, "open(name, hash_size=0, tdb_flags=TDB_DEFAULT, flags=O_RDWR, mode=0600, log_fn=None)\n"
"Open a TDB file." },
{ NULL }
};
@@ -846,6 +926,11 @@ void inittdb(void)
PyModule_AddObject(m, "DISALLOW_NESTING", PyInt_FromLong(TDB_DISALLOW_NESTING));
PyModule_AddObject(m, "INCOMPATIBLE_HASH", PyInt_FromLong(TDB_INCOMPATIBLE_HASH));
+ PyModule_AddObject(m, "DEBUG_FATAL", PyInt_FromLong(TDB_DEBUG_FATAL));
+ PyModule_AddObject(m, "DEBUG_ERROR", PyInt_FromLong(TDB_DEBUG_ERROR));
+ PyModule_AddObject(m, "DEBUG_WARNING", PyInt_FromLong(TDB_DEBUG_WARNING));
+ PyModule_AddObject(m, "DEBUG_TRACE", PyInt_FromLong(TDB_DEBUG_TRACE));
+
PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText"));
Py_INCREF(&PyTdb);
diff --git a/lib/tdb/python/tests/simple.py b/lib/tdb/python/tests/simple.py
index d0abaad..b9dbb48 100644
--- a/lib/tdb/python/tests/simple.py
+++ b/lib/tdb/python/tests/simple.py
@@ -258,6 +258,23 @@ class SimpleTdbTests(TestCase):
self.tdb.add_flags(tdb.NOMMAP)
self.tdb.remove_flags(tdb.NOMMAP)
+ def test_log(self):
+ self.assertEquals(None, self.tdb.log_fn())
+
+ v = []
+ def logit(db, level, msg):
+ v.append( (level, msg) )
+
+ self.tdb.set_logging_function(logit)
+
+ self.tdb.transaction_start()
+ self.assertEquals(v, [])
+
+ # so far the only place in tdb which logs at TRACE level on non-failure
+ self.tdb.transaction_start()
+ self.assertEquals(len(v), 1)
+ self.assertEquals(v[0], (tdb.DEBUG_TRACE, "tdb_transaction_start: nesting 1\n"))
+
if __name__ == '__main__':
import unittest
--
1.7.3.1.50.g1e633
More information about the samba-technical
mailing list