diff options
| author | David Strauss <david@davidstrauss.net> | 2012-09-03 15:43:06 -0700 |
|---|---|---|
| committer | David Strauss <david@davidstrauss.net> | 2012-09-03 15:43:06 -0700 |
| commit | 6f3626418964aa2f6dcc91ecebebd369beed1606 (patch) | |
| tree | 05a9e9b65627bd261bd53d12958f210a4c23c72a | |
| parent | bc56ace896c824492aada154e32de498ee07bffc (diff) | |
| parent | 6f190657c0f0ec1769d0c45213796370788515db (diff) | |
| download | python-systemd-6f3626418964aa2f6dcc91ecebebd369beed1606.tar.gz | |
Merge pull request #2 from keszybz/master
Python 3 and UTF-8
| -rw-r--r-- | .dir-locals.el | 7 | ||||
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | README.md | 37 | ||||
| -rw-r--r-- | journald.c | 52 | ||||
| -rw-r--r-- | journald/__init__.py | 54 | ||||
| -rw-r--r-- | journald/_journald.c | 111 | ||||
| -rw-r--r-- | setup.py | 9 |
7 files changed, 208 insertions, 64 deletions
diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..8bccaf0 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,7 @@ +; Sets emacs variables based on mode. +; A list of (major-mode . ((var1 . value1) (var2 . value2))) +; Mode can be nil, which gives default values. + +((nil . ((indent-tabs-mode . nil) + (c-basic-offset . 4))) +) @@ -1,4 +1,6 @@ +__pycache__/ *.py[co] +/journald/*.so # Packages *.egg @@ -1,7 +1,9 @@ journald-python =============== -Python module for native access to the journald facilities in recent versions of systemd. In particular, this capability includes passing key/value pairs as fields that journald can use for filtering. +Python module for native access to the journald facilities in recent +versions of systemd. In particular, this capability includes passing +key/value pairs as fields that journald can use for filtering. Installation ============ @@ -17,16 +19,35 @@ Usage Quick example: import journald - journald.send('MESSAGE=Hello world.') - journald.send('MESSAGE=Hello, again, world.', 'FIELD2=Greetings!', 'FIELD3=Guten tag.') - journald.send('ARBITRARY=anything', 'FIELD3=Greetings!') + journald.send('Hello world') + journald.send('Hello, again, world', FIELD2='Greetings!', FIELD3='Guten tag') + journald.send('Binary message', BINARY=b'\xde\xad\xbe\xef') + +There is one required argument -- the message, and additional fields +can be specified as keyword arguments. Following the journald API, all +names are uppercase. + +The journald sendv call can also be accessed directly: + + import journald + journald.sendv('MESSAGE=Hello world') + journald.sendv('MESSAGE=Hello, again, world', 'FIELD2=Greetings!', + 'FIELD3=Guten tag') + journald.sendv('MESSAGE=Binary message', b'BINARY=\xde\xad\xbe\xef') + +The two examples should give the same results in the log. Notes: - * Each argument must be in the form of a KEY=value pair, environmental variable style. - * Unlike the native C version of journald's sd_journal_send(), printf-style substitution is not supported. Perform any substitution using Python's % operator or .format() capabilities first. - * The base message is usually sent in the form MESSAGE=hello. The MESSAGE field is, however, not required. - * Invalid or zero arguments results in nothing recorded in journald. + * Unlike the native C version of journald's sd_journal_send(), + printf-style substitution is not supported. Perform any + substitution using Python's % operator or .format() capabilities + first. + * The base message is usually sent in the form MESSAGE=hello. The + MESSAGE field is, however, not required. + * A ValueError is thrown is thrown if sd_journald_sendv() results in + an error. This might happen if there are no arguments or one of them + is invalid. Viewing Output ============== diff --git a/journald.c b/journald.c deleted file mode 100644 index b106c52..0000000 --- a/journald.c +++ /dev/null @@ -1,52 +0,0 @@ -#include <Python.h> -#include <systemd/sd-journal.h> - -static PyObject * -journald_send(PyObject *self, PyObject *args) { - struct iovec *iov = NULL; - int argc = PyTuple_Size(args); - int i; - - // Allocate sufficient iovector space for the arguments. - iov = malloc(argc * sizeof(struct iovec)); - if (!iov) { - return PyErr_NoMemory(); - } - - // Iterate through the Python arguments and fill the iovector. - for (i = 0; i < argc; ++i) { - PyObject *item = PyTuple_GetItem(args, i); - char * stritem = PyString_AsString(item); - if (stritem == NULL) { - // PyString_AsString has already raised TypeError at this - // point. We can just free iov and return NULL. - free(iov); - return NULL; - } - iov[i].iov_base = stritem; - iov[i].iov_len = strlen(stritem); - } - - // Send the iovector to journald. - sd_journal_sendv(iov, argc); - - // Free the iovector. The actual strings - // are already managed by Python. - free(iov); - - // End with success. - Py_INCREF(Py_None); - return Py_None; -} - -static PyMethodDef journaldMethods[] = { - {"send", journald_send, METH_VARARGS, - "Send an entry to journald."}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -PyMODINIT_FUNC -initjournald(void) -{ - (void) Py_InitModule("journald", journaldMethods); -} diff --git a/journald/__init__.py b/journald/__init__.py new file mode 100644 index 0000000..bcf682b --- /dev/null +++ b/journald/__init__.py @@ -0,0 +1,54 @@ +from ._journald import sendv +import traceback as _traceback + +def _make_line(field, value): + if isinstance(value, bytes): + return field.encode('utf-8') + b'=' + value + else: + return field + '=' + value + +def send(MESSAGE, MESSAGE_ID=None, + CODE_FILE=None, CODE_LINE=None, CODE_FUNC=None, + **kwargs): + r"""Send a message to journald. + + >>> journald.send('Hello world') + >>> journald.send('Hello, again, world', FIELD2='Greetings!') + >>> journald.send('Binary message', BINARY=b'\xde\xad\xbe\xef') + + Value of the MESSAGE argument will be used for the MESSAGE= field. + + MESSAGE_ID can be given to uniquely identify the type of message. + + Other parts of the message can be specified as keyword arguments. + + Both MESSAGE and MESSAGE_ID, if present, must be strings, and will + be sent as UTF-8 to journald. Other arguments can be bytes, in + which case they will be sent as-is to journald. + + CODE_LINE, CODE_FILE, and CODE_FUNC can be specified to identify + the caller. Unless at least on of the three is given, values are + extracted from the stack frame of the caller of send(). CODE_FILE + and CODE_FUNC must be strings, CODE_LINE must be an integer. + + Other useful fields include PRIORITY, SYSLOG_FACILITY, + SYSLOG_IDENTIFIER, SYSLOG_PID. + """ + + args = ['MESSAGE=' + MESSAGE] + + if MESSAGE_ID is not None: + args.append('MESSAGE_ID=' + MESSAGE_ID) + + if CODE_LINE == CODE_FILE == CODE_FUNC == None: + CODE_FILE, CODE_LINE, CODE_FUNC = \ + _traceback.extract_stack(limit=2)[0][:3] + if CODE_FILE is not None: + args.append('CODE_FILE=' + CODE_FILE) + if CODE_LINE is not None: + args.append('CODE_LINE={:d}'.format(CODE_LINE)) + if CODE_FUNC is not None: + args.append('CODE_FUNC=' + CODE_FUNC) + + args.extend(_make_line(key, val) for key, val in kwargs.items()) + return sendv(*args) diff --git a/journald/_journald.c b/journald/_journald.c new file mode 100644 index 0000000..82b3b61 --- /dev/null +++ b/journald/_journald.c @@ -0,0 +1,111 @@ +#include <Python.h> +#define SD_JOURNAL_SUPPRESS_LOCATION +#include <systemd/sd-journal.h> + +PyDoc_STRVAR(journald_sendv__doc__, + "sendv('FIELD=value', 'FIELD=value', ...) -> None\n\n" + "Send an entry to journald." + ); + +static PyObject * +journald_sendv(PyObject *self, PyObject *args) { + struct iovec *iov = NULL; + int argc = PyTuple_Size(args); + int i, r; + PyObject *ret = NULL; + + PyObject **encoded = calloc(argc, sizeof(PyObject*)); + if (!encoded) { + ret = PyErr_NoMemory(); + goto out1; + } + + // Allocate sufficient iovector space for the arguments. + iov = malloc(argc * sizeof(struct iovec)); + if (!iov) { + ret = PyErr_NoMemory(); + goto out; + } + + // Iterate through the Python arguments and fill the iovector. + for (i = 0; i < argc; ++i) { + PyObject *item = PyTuple_GetItem(args, i); + char *stritem; + Py_ssize_t length; + + if (PyUnicode_Check(item)) { + encoded[i] = PyUnicode_AsEncodedString(item, "utf-8", "strict"); + if (encoded[i] == NULL) + goto out; + item = encoded[i]; + } + if (PyBytes_AsStringAndSize(item, &stritem, &length)) + goto out; + + iov[i].iov_base = stritem; + iov[i].iov_len = length; + } + + // Clear errno, because sd_journal_sendv will not set it by + // itself, unless an error occurs in one of the system calls. + errno = 0; + + // Send the iovector to journald. + r = sd_journal_sendv(iov, argc); + + if (r) { + if (errno) + PyErr_SetFromErrno(PyExc_IOError); + else + PyErr_SetString(PyExc_ValueError, "invalid message format"); + goto out; + } + + // End with success. + Py_INCREF(Py_None); + ret = Py_None; + +out: + for (i = 0; i < argc; ++i) + Py_XDECREF(encoded[i]); + + free(encoded); + +out1: + // Free the iovector. The actual strings + // are already managed by Python. + free(iov); + + return ret; +} + +static PyMethodDef methods[] = { + {"sendv", journald_sendv, METH_VARARGS, journald_sendv__doc__}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +#if PY_MAJOR_VERSION < 3 + +PyMODINIT_FUNC +init_journald(void) +{ + (void) Py_InitModule("_journald", methods); +} + +#else + +static struct PyModuleDef module = { + PyModuleDef_HEAD_INIT, + "_journald", /* name of module */ + NULL, /* module documentation, may be NULL */ + 0, /* size of per-interpreter state of the module */ + methods +}; + +PyMODINIT_FUNC +PyInit__journald(void) +{ + return PyModule_Create(&module); +} + +#endif @@ -1,12 +1,13 @@ from distutils.core import setup, Extension -journald = Extension('journald', - libraries = ['systemd-journal'], - sources = ['journald.c']) +cjournald = Extension('journald/_journald', + libraries = ['systemd-journal'], + sources = ['journald/_journald.c']) setup (name = 'journald', version = '0.1', description = 'Native interface to the journald facilities of systemd', author_email = 'david@davidstrauss.net', url = 'https://github.com/davidstrauss/journald-python', - ext_modules = [journald]) + py_modules = ['journald'], + ext_modules = [cjournald]) |
