From 8d7e170a5230753d8406276f8b5598e5bb6766e6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Sep 2012 04:01:18 +0200 Subject: python: integrate David Strauss' python-systemd package --- src/python-systemd/Makefile | 1 + src/python-systemd/__init__.py | 0 src/python-systemd/_journal.c | 137 +++++++++++++++++++++++++++++++++++++++++ src/python-systemd/journal.py | 93 ++++++++++++++++++++++++++++ 4 files changed, 231 insertions(+) create mode 120000 src/python-systemd/Makefile create mode 100644 src/python-systemd/__init__.py create mode 100644 src/python-systemd/_journal.c create mode 100644 src/python-systemd/journal.py (limited to 'src/python-systemd') diff --git a/src/python-systemd/Makefile b/src/python-systemd/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/python-systemd/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/python-systemd/__init__.py b/src/python-systemd/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c new file mode 100644 index 0000000000..3e10981707 --- /dev/null +++ b/src/python-systemd/_journal.c @@ -0,0 +1,137 @@ +#include + +#define SD_JOURNAL_SUPPRESS_LOCATION +#include + +#include "macro.h" + +PyDoc_STRVAR(journal_sendv__doc__, + "sendv('FIELD=value', 'FIELD=value', ...) -> None\n\n" + "Send an entry to the journal." + ); + +static PyObject * +journal_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 the journal. + 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; +} + +PyDoc_STRVAR(journal_stream_fd__doc__, + "stream_fd(identifier, priority, level_prefix) -> fd\n\n" + "Open a stream to journal by calling sd_journal_stream_fd(3)." + ); + +static PyObject* +journal_stream_fd(PyObject *self, PyObject *args) { + const char* identifier; + int priority, level_prefix; + int fd; + if (!PyArg_ParseTuple(args, "sii:stream_fd", + &identifier, &priority, &level_prefix)) + return NULL; + + fd = sd_journal_stream_fd(identifier, priority, level_prefix); + if (fd < 0) + return PyErr_SetFromErrno(PyExc_IOError); + + return PyLong_FromLong(fd); +} + +static PyMethodDef methods[] = { + {"sendv", journal_sendv, METH_VARARGS, journal_sendv__doc__}, + {"stream_fd", journal_stream_fd, METH_VARARGS, + journal_stream_fd__doc__}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +#if PY_MAJOR_VERSION < 3 + +PyMODINIT_FUNC +init_journal(void) +{ + (void) Py_InitModule("_journal", methods); +} + +#else + +static struct PyModuleDef module = { + PyModuleDef_HEAD_INIT, + "_journal", /* name of module */ + NULL, /* module documentation, may be NULL */ + 0, /* size of per-interpreter state of the module */ + methods +}; + +PyMODINIT_FUNC +PyInit__journal(void) +{ + return PyModule_Create(&module); +} + +#endif diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py new file mode 100644 index 0000000000..53e992bed3 --- /dev/null +++ b/src/python-systemd/journal.py @@ -0,0 +1,93 @@ +import traceback as _traceback +import os as _os +from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, + LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) +from ._journal import sendv, stream_fd + +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. + + >>> journal.send('Hello world') + >>> journal.send('Hello, again, world', FIELD2='Greetings!') + >>> journal.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 journal. Other arguments can be bytes, in + which case they will be sent as-is to journal. + + 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) + +def stream(identifier, priority=LOG_DEBUG, level_prefix=False): + r"""Return a file object wrapping a stream to journal. + + Log messages written to this file as simple newline sepearted + text strings are written to the journal. + + The file will be line buffered, so messages are actually sent + after a newline character is written. + + >>> stream = journal.stream('myapp') + >>> stream + ', mode 'w' at 0x...> + >>> stream.write('message...\n') + + will produce the following message in the journal: + + PRIORITY=7 + SYSLOG_IDENTIFIER=myapp + MESSAGE=message... + + Using the interface with print might be more convinient: + + >>> from __future__ import print_function + >>> print('message...', file=stream) + + priority is the syslog priority, one of LOG_EMERG, LOG_ALERT, + LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG. + + level_prefix is a boolean. If true, kernel-style log priority + level prefixes (such as '<1>') are interpreted. See sd-daemon(3) + for more information. + """ + + fd = stream_fd(identifier, priority, level_prefix) + return _os.fdopen(fd, 'w', 1) -- cgit v1.2.3-54-g00ecf From 6b91ae13f2b0792a21603861672594ed9bb9cd41 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Sep 2012 04:05:28 +0200 Subject: python: change license to LGPL 2.1 The original license has been MIT for this code, but David Strauss (its original author) agreed to relicense it to LGPL 2.1 for inclusion in systemd. --- make-man-index.py | 18 ++++++++++++++++++ src/python-systemd/__init__.py | 18 ++++++++++++++++++ src/python-systemd/_journal.c | 21 +++++++++++++++++++++ src/python-systemd/journal.py | 19 +++++++++++++++++++ 4 files changed, 76 insertions(+) (limited to 'src/python-systemd') diff --git a/make-man-index.py b/make-man-index.py index 8789d995ec..56f38ce413 100755 --- a/make-man-index.py +++ b/make-man-index.py @@ -1,4 +1,22 @@ #!/usr/bin/env python +# -*- Mode: python; indent-tabs-mode: nil -*- */ +# +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . from xml.etree.ElementTree import parse, Element, SubElement, tostring from sys import argv, stdout diff --git a/src/python-systemd/__init__.py b/src/python-systemd/__init__.py index e69de29bb2..0d56b992f4 100644 --- a/src/python-systemd/__init__.py +++ b/src/python-systemd/__init__.py @@ -0,0 +1,18 @@ +# -*- Mode: python; indent-tabs-mode: nil -*- */ +# +# This file is part of systemd. +# +# Copyright 2012 David Strauss +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c index 3e10981707..64310a729a 100644 --- a/src/python-systemd/_journal.c +++ b/src/python-systemd/_journal.c @@ -1,3 +1,24 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 David Strauss + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + #include #define SD_JOURNAL_SUPPRESS_LOCATION diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py index 53e992bed3..0f8a330305 100644 --- a/src/python-systemd/journal.py +++ b/src/python-systemd/journal.py @@ -1,3 +1,22 @@ +# -*- Mode: python; indent-tabs-mode: nil -*- */ +# +# This file is part of systemd. +# +# Copyright 2012 David Strauss +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + import traceback as _traceback import os as _os from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, -- cgit v1.2.3-54-g00ecf From 0aee68ad028e696934367045e652a65865c0de52 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Sep 2012 04:16:10 +0200 Subject: python: reindent to follow coding style --- Makefile.am | 1 + src/python-systemd/_journal.c | 186 ++++++++++++++++++++---------------------- src/python-systemd/journal.py | 122 +++++++++++++-------------- 3 files changed, 154 insertions(+), 155 deletions(-) (limited to 'src/python-systemd') diff --git a/Makefile.am b/Makefile.am index be97193b45..8885ab83d1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3419,6 +3419,7 @@ _journal_la_SOURCES = \ _journal_la_CFLAGS = \ $(AM_CFLAGS) \ -fvisibility=default \ + -Wno-missing-prototypes \ $(PYTHON_CFLAGS) _journal_la_LDFLAGS = \ diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c index 64310a729a..c305b77ce2 100644 --- a/src/python-systemd/_journal.c +++ b/src/python-systemd/_journal.c @@ -24,135 +24,129 @@ #define SD_JOURNAL_SUPPRESS_LOCATION #include -#include "macro.h" - PyDoc_STRVAR(journal_sendv__doc__, "sendv('FIELD=value', 'FIELD=value', ...) -> None\n\n" "Send an entry to the journal." - ); - -static PyObject * -journal_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; - } +static PyObject *journal_sendv(PyObject *self, PyObject *args) { + struct iovec *iov = NULL; + int argc; + int i, r; + PyObject *ret = NULL; + PyObject **encoded; - // Clear errno, because sd_journal_sendv will not set it by - // itself, unless an error occurs in one of the system calls. - errno = 0; + argc = PyTuple_Size(args); - // Send the iovector to the journal. - r = sd_journal_sendv(iov, argc); + encoded = calloc(argc, sizeof(PyObject*)); + if (!encoded) { + ret = PyErr_NoMemory(); + goto out1; + } - if (r) { - if (errno) - PyErr_SetFromErrno(PyExc_IOError); - else - PyErr_SetString(PyExc_ValueError, "invalid message format"); - goto out; - } + /* Allocate sufficient iovector space for the arguments. */ + iov = malloc(argc * sizeof(struct iovec)); + if (!iov) { + ret = PyErr_NoMemory(); + goto out; + } - // End with success. - Py_INCREF(Py_None); - ret = Py_None; + /* 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 the journal. */ + 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]); + for (i = 0; i < argc; ++i) + Py_XDECREF(encoded[i]); - free(encoded); + free(encoded); out1: - // Free the iovector. The actual strings - // are already managed by Python. - free(iov); + /* Free the iovector. The actual strings + are already managed by Python. */ + free(iov); - return ret; + return ret; } PyDoc_STRVAR(journal_stream_fd__doc__, "stream_fd(identifier, priority, level_prefix) -> fd\n\n" "Open a stream to journal by calling sd_journal_stream_fd(3)." - ); - -static PyObject* -journal_stream_fd(PyObject *self, PyObject *args) { - const char* identifier; - int priority, level_prefix; - int fd; - if (!PyArg_ParseTuple(args, "sii:stream_fd", - &identifier, &priority, &level_prefix)) - return NULL; - - fd = sd_journal_stream_fd(identifier, priority, level_prefix); - if (fd < 0) - return PyErr_SetFromErrno(PyExc_IOError); - - return PyLong_FromLong(fd); +); + +static PyObject* journal_stream_fd(PyObject *self, PyObject *args) { + const char* identifier; + int priority, level_prefix; + int fd; + + if (!PyArg_ParseTuple(args, "sii:stream_fd", + &identifier, &priority, &level_prefix)) + return NULL; + + fd = sd_journal_stream_fd(identifier, priority, level_prefix); + if (fd < 0) + return PyErr_SetFromErrno(PyExc_IOError); + + return PyLong_FromLong(fd); } static PyMethodDef methods[] = { - {"sendv", journal_sendv, METH_VARARGS, journal_sendv__doc__}, - {"stream_fd", journal_stream_fd, METH_VARARGS, - journal_stream_fd__doc__}, - {NULL, NULL, 0, NULL} /* Sentinel */ + { "sendv", journal_sendv, METH_VARARGS, journal_sendv__doc__ }, + { "stream_fd", journal_stream_fd, METH_VARARGS, journal_stream_fd__doc__ }, + { NULL, NULL, 0, NULL } /* Sentinel */ }; #if PY_MAJOR_VERSION < 3 -PyMODINIT_FUNC -init_journal(void) -{ - (void) Py_InitModule("_journal", methods); +PyMODINIT_FUNC init_journal(void) { + (void) Py_InitModule("_journal", methods); } #else static struct PyModuleDef module = { - PyModuleDef_HEAD_INIT, - "_journal", /* name of module */ - NULL, /* module documentation, may be NULL */ - 0, /* size of per-interpreter state of the module */ - methods + PyModuleDef_HEAD_INIT, + "_journal", /* name of module */ + NULL, /* module documentation, may be NULL */ + 0, /* size of per-interpreter state of the module */ + methods }; -PyMODINIT_FUNC -PyInit__journal(void) -{ - return PyModule_Create(&module); +PyMODINIT_FUNC PyInit__journal(void) { + return PyModule_Create(&module); } #endif diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py index 0f8a330305..760d2db014 100644 --- a/src/python-systemd/journal.py +++ b/src/python-systemd/journal.py @@ -24,89 +24,93 @@ from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, from ._journal import sendv, stream_fd def _make_line(field, value): - if isinstance(value, bytes): - return field.encode('utf-8') + b'=' + value - else: - return 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. + r"""Send a message to journald. - >>> journal.send('Hello world') - >>> journal.send('Hello, again, world', FIELD2='Greetings!') - >>> journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef') + >>> journal.send('Hello world') + >>> journal.send('Hello, again, world', FIELD2='Greetings!') + >>> journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef') - Value of the MESSAGE argument will be used for the MESSAGE= field. + Value of the MESSAGE argument will be used for the MESSAGE= + field. - MESSAGE_ID can be given to uniquely identify the type of message. + MESSAGE_ID can be given to uniquely identify the type of + message. - Other parts of the message can be specified as keyword arguments. + 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 journal. Other arguments can be bytes, in - which case they will be sent as-is to journal. + Both MESSAGE and MESSAGE_ID, if present, must be strings, and + will be sent as UTF-8 to journal. Other arguments can be + bytes, in which case they will be sent as-is to journal. - 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. + 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. - """ + Other useful fields include PRIORITY, SYSLOG_FACILITY, + SYSLOG_IDENTIFIER, SYSLOG_PID. + """ - args = ['MESSAGE=' + MESSAGE] + args = ['MESSAGE=' + MESSAGE] - if MESSAGE_ID is not None: - args.append('MESSAGE_ID=' + MESSAGE_ID) + 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) + 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) + args.extend(_make_line(key, val) for key, val in kwargs.items()) + return sendv(*args) def stream(identifier, priority=LOG_DEBUG, level_prefix=False): - r"""Return a file object wrapping a stream to journal. + r"""Return a file object wrapping a stream to journal. - Log messages written to this file as simple newline sepearted - text strings are written to the journal. + Log messages written to this file as simple newline sepearted + text strings are written to the journal. - The file will be line buffered, so messages are actually sent - after a newline character is written. + The file will be line buffered, so messages are actually sent + after a newline character is written. - >>> stream = journal.stream('myapp') - >>> stream - ', mode 'w' at 0x...> - >>> stream.write('message...\n') + >>> stream = journal.stream('myapp') + >>> stream + ', mode 'w' at 0x...> + >>> stream.write('message...\n') - will produce the following message in the journal: + will produce the following message in the journal: - PRIORITY=7 - SYSLOG_IDENTIFIER=myapp - MESSAGE=message... + PRIORITY=7 + SYSLOG_IDENTIFIER=myapp + MESSAGE=message... - Using the interface with print might be more convinient: + Using the interface with print might be more convinient: - >>> from __future__ import print_function - >>> print('message...', file=stream) + >>> from __future__ import print_function + >>> print('message...', file=stream) - priority is the syslog priority, one of LOG_EMERG, LOG_ALERT, - LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG. + priority is the syslog priority, one of LOG_EMERG, LOG_ALERT, + LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG. - level_prefix is a boolean. If true, kernel-style log priority - level prefixes (such as '<1>') are interpreted. See sd-daemon(3) - for more information. - """ + level_prefix is a boolean. If true, kernel-style log priority + level prefixes (such as '<1>') are interpreted. See + sd-daemon(3) for more information. + """ - fd = stream_fd(identifier, priority, level_prefix) - return _os.fdopen(fd, 'w', 1) + fd = stream_fd(identifier, priority, level_prefix) + return _os.fdopen(fd, 'w', 1) -- cgit v1.2.3-54-g00ecf From c4164442defa56dfa92a6b0fa5d49d8876e0ebb7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Sep 2012 04:25:59 +0200 Subject: python: fix error handling, and allocate argument array on the stack --- src/python-systemd/_journal.c | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) (limited to 'src/python-systemd') diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c index c305b77ce2..eab9c2982c 100644 --- a/src/python-systemd/_journal.c +++ b/src/python-systemd/_journal.c @@ -21,6 +21,8 @@ #include +#include + #define SD_JOURNAL_SUPPRESS_LOCATION #include @@ -36,20 +38,13 @@ static PyObject *journal_sendv(PyObject *self, PyObject *args) { PyObject *ret = NULL; PyObject **encoded; + /* Allocate an array for the argument strings */ argc = PyTuple_Size(args); - - encoded = calloc(argc, sizeof(PyObject*)); - if (!encoded) { - ret = PyErr_NoMemory(); - goto out1; - } + encoded = alloca(argc * sizeof(PyObject*)); + memset(encoded, 0, argc * sizeof(PyObject*)); /* Allocate sufficient iovector space for the arguments. */ - iov = malloc(argc * sizeof(struct iovec)); - if (!iov) { - ret = PyErr_NoMemory(); - goto out; - } + iov = alloca(argc * sizeof(struct iovec)); /* Iterate through the Python arguments and fill the iovector. */ for (i = 0; i < argc; ++i) { @@ -70,17 +65,11 @@ static PyObject *journal_sendv(PyObject *self, PyObject *args) { 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 the journal. */ r = sd_journal_sendv(iov, argc); - if (r) { - if (errno) - PyErr_SetFromErrno(PyExc_IOError); - else - PyErr_SetString(PyExc_ValueError, "invalid message format"); + if (r < 0) { + errno = -r; + PyErr_SetFromErrno(PyExc_IOError); goto out; } @@ -92,13 +81,6 @@ 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; } @@ -117,8 +99,10 @@ static PyObject* journal_stream_fd(PyObject *self, PyObject *args) { return NULL; fd = sd_journal_stream_fd(identifier, priority, level_prefix); - if (fd < 0) + if (fd < 0) { + errno = -fd; return PyErr_SetFromErrno(PyExc_IOError); + } return PyLong_FromLong(fd); } -- cgit v1.2.3-54-g00ecf From ff89a42a8602443b9a74be276cf3ee351b3914c7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Sep 2012 19:34:09 +0200 Subject: python: make gcc shut up --- Makefile.am | 1 - src/python-systemd/_journal.c | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src/python-systemd') diff --git a/Makefile.am b/Makefile.am index 8885ab83d1..be97193b45 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3419,7 +3419,6 @@ _journal_la_SOURCES = \ _journal_la_CFLAGS = \ $(AM_CFLAGS) \ -fvisibility=default \ - -Wno-missing-prototypes \ $(PYTHON_CFLAGS) _journal_la_LDFLAGS = \ diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c index eab9c2982c..d27178d666 100644 --- a/src/python-systemd/_journal.c +++ b/src/python-systemd/_journal.c @@ -113,6 +113,9 @@ static PyMethodDef methods[] = { { NULL, NULL, 0, NULL } /* Sentinel */ }; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_journal(void) { @@ -133,4 +136,6 @@ PyMODINIT_FUNC PyInit__journal(void) { return PyModule_Create(&module); } +#pragma GCC diagnostic pop + #endif -- cgit v1.2.3-54-g00ecf From c94f4b8b53aad25ba364bb79fc53c5b79de28088 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Thu, 1 Nov 2012 00:10:47 +0100 Subject: systemd-python: fix nesting of #ifs and #pragmas --- src/python-systemd/_journal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/python-systemd') diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c index d27178d666..0bdf709aea 100644 --- a/src/python-systemd/_journal.c +++ b/src/python-systemd/_journal.c @@ -136,6 +136,6 @@ PyMODINIT_FUNC PyInit__journal(void) { return PyModule_Create(&module); } -#pragma GCC diagnostic pop - #endif + +#pragma GCC diagnostic pop -- cgit v1.2.3-54-g00ecf From 73c0495f6830ca99c0c6eb15e83c3c7d44096d19 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Tue, 9 Oct 2012 18:12:02 +0300 Subject: python: add journal backend for the logging framework Supports Python versions 2.6 through 3.3 (tested on 2.7 and 3.2). See JournalHandler docstring for usage details. [zj: - use send() instead of using sendv() directly - do exception handling like in the logging module - bumped min version to python2.6, since the module does not work with python2.5 anyway ] --- src/python-systemd/journal.py | 85 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) (limited to 'src/python-systemd') diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py index 760d2db014..516ca1ab56 100644 --- a/src/python-systemd/journal.py +++ b/src/python-systemd/journal.py @@ -19,6 +19,7 @@ import traceback as _traceback import os as _os +import logging as _logging from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) from ._journal import sendv, stream_fd @@ -114,3 +115,87 @@ def stream(identifier, priority=LOG_DEBUG, level_prefix=False): fd = stream_fd(identifier, priority, level_prefix) return _os.fdopen(fd, 'w', 1) + +class JournalHandler(_logging.Handler): + """Journal handler class for the Python logging framework. + + Please see the Python logging module documentation for an + overview: http://docs.python.org/library/logging.html + + To create a custom logger whose messages go only to journal: + + >>> log = logging.getLogger('custom_logger_name') + >>> log.propagate = False + >>> log.addHandler(journal.JournalHandler()) + >>> log.warn("Some message: %s", detail) + + Note that by default, message levels INFO and DEBUG are ignored + by the logging framework. To enable those log levels: + + >>> log.setLevel(logging.DEBUG) + + To attach journal MESSAGE_ID, an extra field is supported: + + >>> log.warn("Message with ID", + >>> extra={'MESSAGE_ID': '22bb01335f724c959ac4799627d1cb61'}) + + To redirect all logging messages to journal regardless of where + they come from, attach it to the root logger: + + >>> logging.root.addHandler(journal.JournalHandler()) + + For more complex configurations when using dictConfig or + fileConfig, specify 'systemd.journal.JournalHandler' as the + handler class. Only standard handler configuration options + are supported: level, formatter, filters. + + The following journal fields will be sent: + + MESSAGE, PRIORITY, THREAD_NAME, CODE_FILE, CODE_LINE, + CODE_FUNC, LOGGER (name as supplied to getLogger call), + MESSAGE_ID (optional, see above). + """ + + def emit(self, record): + """Write record as journal event. + + MESSAGE is taken from the message provided by the + user, and PRIORITY, LOGGER, THREAD_NAME, + CODE_{FILE,LINE,FUNC} fields are appended + automatically. In addition, record.MESSAGE_ID will be + used if present. + """ + try: + msg = self.format(record) + pri = self.mapPriority(record.levelno) + mid = getattr(record, 'MESSAGE_ID', None) + send(msg, + MESSAGE_ID=mid, + PRIORITY=format(pri), + LOGGER=record.name, + THREAD_NAME=record.threadName, + CODE_FILE=record.pathname, + CODE_LINE=record.lineno, + CODE_FUNC=record.funcName) + except Exception: + self.handleError(record) + + @staticmethod + def mapPriority(levelno): + """Map logging levels to journald priorities. + + Since Python log level numbers are "sparse", we have + to map numbers in between the standard levels too. + """ + if levelno <= _logging.DEBUG: + return LOG_DEBUG + elif levelno <= _logging.INFO: + return LOG_INFO + elif levelno <= _logging.WARNING: + return LOG_WARNING + elif levelno <= _logging.ERROR: + return LOG_ERR + elif levelno <= _logging.CRITICAL: + return LOG_CRIT + else: + return LOG_ALERT -- cgit v1.2.3-54-g00ecf