diff --git a/_pylibmcmodule.c b/_pylibmcmodule.c index da689bd..1f812e4 100644 --- a/_pylibmcmodule.c +++ b/_pylibmcmodule.c @@ -742,6 +742,98 @@ error: return NULL; } +static PyObject *PylibMC_Client_get_stats(PylibMC_Client *self, PyObject *args) { + memcached_stat_st *stats; + memcached_return rc; + char *mc_args; + PyObject *retval; + Py_ssize_t nservers, serveridx; + memcached_server_st *servers; + + mc_args = NULL; + if (!PyArg_ParseTuple(args, "|s", &mc_args)) + return NULL; + + stats = memcached_stat(self->mc, mc_args, &rc); + if (rc != MEMCACHED_SUCCESS) + return PylibMC_ErrFromMemcached(self, "get_stats", rc); + + nservers = (Py_ssize_t)memcached_server_count(self->mc); + servers = memcached_server_list(self->mc); + retval = PyList_New(nservers); + + /** retval contents: + * [(' (), + * {stat: stat, stat: stat}), + * (str, dict), + * (str, dict)] + */ + + for (serveridx = 0; serveridx < nservers; serveridx++) { + memcached_server_st *server; + memcached_stat_st *stat; + memcached_return rc; + PyObject *desc, *val; + char **stat_keys = NULL; + char **curr_key; + + server = servers + serveridx; + stat = stats + serveridx; + + val = PyDict_New(); + if (val == NULL) + goto error; + + stat_keys = memcached_stat_get_keys(self->mc, stat, &rc); + if (rc != MEMCACHED_SUCCESS) + goto loop_error; + + for (curr_key = stat_keys; *curr_key; curr_key++) { + PyObject *curr_value; + char *mc_val; + memcached_return get_val_rc; + int fail; + + mc_val = memcached_stat_get_value(self->mc, stat, *curr_key, + &get_val_rc); + if (get_val_rc != MEMCACHED_SUCCESS) { + PylibMC_ErrFromMemcached(self, "get_stats val", get_val_rc); + goto loop_error; + } + + curr_value = PyString_FromString(mc_val); + free(mc_val); + if (curr_value == NULL) + goto loop_error; + + fail = PyDict_SetItemString(val, *curr_key, curr_value); + Py_DECREF(curr_value); + if (fail) + goto loop_error; + } + + free(stat_keys); + + desc = PyString_FromFormat("%s:%d (%u)", + server->hostname, server->port, + (unsigned int)serveridx); + PyList_SET_ITEM(retval, serveridx, Py_BuildValue("NN", desc, val)); + continue; +loop_error: + free(stat_keys); + Py_DECREF(val); + goto error; + } + + free(stats); + + return retval; +error: + Py_DECREF(retval); + free(stats); + return NULL; +} + static PyObject *PylibMC_Client_flush_all(PylibMC_Client *self, PyObject *args, PyObject *kwds) { memcached_return rc; @@ -915,6 +1007,8 @@ Oh, and: plankton.\n"); PylibMCExc_MemcachedError = PyErr_NewException( "_pylibmc.MemcachedError", NULL, NULL); + PyModule_AddObject(module, "MemcachedError", + (PyObject *)PylibMCExc_MemcachedError); PyModule_AddStringConstant(module, "__version__", PYLIBMC_VERSION); diff --git a/_pylibmcmodule.h b/_pylibmcmodule.h index 282145f..a0a39a3 100644 --- a/_pylibmcmodule.h +++ b/_pylibmcmodule.h @@ -153,6 +153,7 @@ static PyObject *PylibMC_Client_set_multi(PylibMC_Client *, PyObject *, PyObject static PyObject *PylibMC_Client_delete_multi(PylibMC_Client *, PyObject *, PyObject *); static PyObject *PylibMC_Client_get_behaviors(PylibMC_Client *); static PyObject *PylibMC_Client_set_behaviors(PylibMC_Client *, PyObject *); +static PyObject *PylibMC_Client_get_stats(PylibMC_Client *, PyObject *); static PyObject *PylibMC_Client_flush_all(PylibMC_Client *, PyObject *, PyObject *); static PyObject *PylibMC_Client_disconnect_all(PylibMC_Client *); static PyObject *PylibMC_Client_clone(PylibMC_Client *); @@ -194,6 +195,8 @@ static PyMethodDef PylibMC_ClientType_methods[] = { "Get behaviors dict."}, {"set_behaviors", (PyCFunction)PylibMC_Client_set_behaviors, METH_O, "Set behaviors dict."}, + {"get_stats", (PyCFunction)PylibMC_Client_get_stats, + METH_VARARGS, "Retrieve statistics from all memcached servers."}, {"flush_all", (PyCFunction)PylibMC_Client_flush_all, METH_VARARGS|METH_KEYWORDS, "Flush all data on all servers."}, {"disconnect_all", (PyCFunction)PylibMC_Client_disconnect_all, METH_NOARGS, diff --git a/pylibmc.py b/pylibmc.py index 5104828..441324d 100644 --- a/pylibmc.py +++ b/pylibmc.py @@ -184,7 +184,7 @@ class ThreadMappedPool(dict): Creates a new client based on the master client if none exists for the current thread. """ - key = threading.current_thread().name + key = threading.current_thread().ident mc = self.pop(key, None) if mc is None: mc = self.master.clone() diff --git a/tests.py b/tests.py index 67a38df..96e60ca 100644 --- a/tests.py +++ b/tests.py @@ -86,6 +86,19 @@ Traceback (most recent call last): ... TypeError: key must be an instance of str +Getting stats is fun! +>>> for (svr, stats) in c.get_stats(): +... print svr +... ks = stats.keys() +... while ks: +... cks, ks = ks[:6], ks[6:] +... print ", ".join(cks) +localhost:11211 (0) +pid, total_items, uptime, version, limit_maxbytes, rusage_user +bytes_read, rusage_system, cmd_get, curr_connections, threads, total_connections +cmd_set, curr_items, get_misses, evictions, bytes, connection_structures +bytes_written, time, pointer_size, get_hits + Also test some flush all. >>> c.set("hi", "guys") True