diff options
| -rw-r--r-- | docs/installation.rst | 1 | ||||
| -rw-r--r-- | docs/release-notes.rst | 1 | ||||
| -rw-r--r-- | docs/release-notes/version-4.5.4.rst | 44 | ||||
| -rw-r--r-- | docs/user-guides.rst | 1 | ||||
| -rw-r--r-- | docs/user-guides/installation-on-macosx.rst | 163 | ||||
| -rw-r--r-- | src/server/__init__.py | 12 | ||||
| -rw-r--r-- | src/server/mod_wsgi.c | 50 | ||||
| -rw-r--r-- | src/server/wsgi_logger.c | 50 | ||||
| -rw-r--r-- | src/server/wsgi_metrics.c | 13 | ||||
| -rw-r--r-- | src/server/wsgi_metrics.h | 4 | ||||
| -rw-r--r-- | src/server/wsgi_thread.c | 2 | ||||
| -rw-r--r-- | src/server/wsgi_thread.h | 1 | ||||
| -rw-r--r-- | src/server/wsgi_version.h | 4 | ||||
| -rw-r--r-- | tests/environ.wsgi | 4 |
14 files changed, 342 insertions, 8 deletions
diff --git a/docs/installation.rst b/docs/installation.rst index 6965e67..6f9351f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -18,6 +18,7 @@ For instructions on how to compile mod_wsgi from source code for UNIX like operating systems such as Linux and MacOS X see: * :doc:`user-guides/quick-installation-guide` + * :doc:`user-guides/installation-on-macosx` If you are on Windows, you should instead use: diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 334d8c7..428e60e 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -5,6 +5,7 @@ Release Notes .. toctree:: :maxdepth: 2 + release-notes/version-4.5.4 release-notes/version-4.5.3 release-notes/version-4.5.2 release-notes/version-4.5.1 diff --git a/docs/release-notes/version-4.5.4.rst b/docs/release-notes/version-4.5.4.rst new file mode 100644 index 0000000..e1836df --- /dev/null +++ b/docs/release-notes/version-4.5.4.rst @@ -0,0 +1,44 @@ +============= +Version 4.5.4 +============= + +Version 4.5.4 of mod_wsgi can be obtained from: + + https://codeload.github.com/GrahamDumpleton/mod_wsgi/tar.gz/4.5.4 + +Bugs Fixed +---------- + +1. When using Apache 2.4 and daemon mode, the connection and request log + IDs from the Apache child worker processes were not being copied across + to the daemon process so that log messages generated against the request + would use the same ID in logs when using the ``%L`` format modifier. + +2. When using Apache 2.4 and daemon mode, the remote client port + information was not being cached such that log messages generated + against the request would use the port in logs when using the ``%a`` + format modifier. + +Features Changed +---------------- + +1. If ``sys.stdout`` and ``sys.stderr`` are used in the context of the + thread handling a request, calls against them to log messages will be + routed back via ``wsgi.errors`` from the per request WSGI ``environ`` + dictionary. This avoids the danger of logged messages from different + request handlers being intermixed as buffering will now be done on a per + request basis. Such messages will also be logged with the correct + connection and request log ID if the ``%L`` formatter is used in the + error log format. + +New Features +------------ + +1. Added new option ``--error-log-format`` to ``mod_wsgi-express`` to allow + the error log message format to be specified. + +2. Pass through to the WSGI per request ``environ`` dictionary new values + for ``mod_wsgi.connection_id`` and ``mod_wsgi.request_id``. These are + the Apache log IDs for the connection and request that it uses in log + messages when using the ``%L`` format modifier. This only applies to + Apache 2.4 and later. diff --git a/docs/user-guides.rst b/docs/user-guides.rst index 60e2c76..7e5b427 100644 --- a/docs/user-guides.rst +++ b/docs/user-guides.rst @@ -6,6 +6,7 @@ User Guides :maxdepth: 1 user-guides/quick-installation-guide + user-guides/installation-on-macosx user-guides/quick-configuration-guide user-guides/configuration-guidelines user-guides/installation-issues diff --git a/docs/user-guides/installation-on-macosx.rst b/docs/user-guides/installation-on-macosx.rst new file mode 100644 index 0000000..0c58047 --- /dev/null +++ b/docs/user-guides/installation-on-macosx.rst @@ -0,0 +1,163 @@ +======================= +Installation On MacOS X +======================= + +If you are using MacOS X, mod_wsgi can be compiled from source code +against the standard versions of Python and Apache httpd server supplied +with the operating system. To do this though you will first need to have +installed the Xcode command line tools. + +The Xcode command line tools package provides a C compiler, along with +header files and support tools for the Apache httpd server. If you have +already set up your system so as to be able to install additional Python +packages which include C extensions, you likely will already have the +Xcode command line tools. + +Install Xcode command line tools +-------------------------------- + +To install the Xcode command line tools you should run the command:: + + xcode-select --install + +If this gives you back the error message:: + + xcode-select: error: command line tools are already installed, use "Software Update" to install updates + +then the tools have already been installed. As noted by the warning +message, do make sure you have run a system software update to ensure +that you have the latest versions of these tools. + +If you do not already have the Xcode command line tools installed, running +that ``xcode-select`` command should result in you being prompted to +install them. This may ask you to provide the details of an administrator +account along with the password for that account. + +Note that it is not necessary to install the whole of the Xcode +developer application from the MacOS X App Store, only the command line +tools using ``xcode-select``. If you have installed the Xcode developer +application, still ensure that the command line tools are installed and +ensure you have run the system software update. + +Configuring and building mod_wsgi +--------------------------------- + +If you are using the Python and Apache httpd server packages provided with +the operating system, all you need to do to configure the mod_wsgi source +code before building it is to run in the mod_wsgi source code directory:: + + ./configure + +This should yield output similar to:: + + checking for apxs2... no + checking for apxs... /usr/sbin/apxs + checking for gcc... gcc + checking whether the C compiler works... yes + checking for C compiler default output file name... a.out + checking for suffix of executables... + checking whether we are cross compiling... no + checking for suffix of object files... o + checking whether we are using the GNU C compiler... yes + checking whether gcc accepts -g... yes + checking for gcc option to accept ISO C89... none needed + checking for prctl... no + checking Apache version... 2.4.18 + checking for python... /usr/bin/python + configure: creating ./config.status + config.status: creating Makefile + +The ``configure`` script should show that it has detected ``apxs`` as being +located at ``/usr/sbin/apxs`` and ``python`` as being at ``/usr/bin/python``. + +If you get different values for ``apxs`` and ``python`` then it means +that you likely have a separate installation of Python or the Apache +httpd server installed on your system. If this is the case, to ensure that +you use the versions of Python and Apache httpd server provided with the +operating system instead use the command:: + + ./configure --with-python=/usr/bin/python --with-apxs=/usr/sbin/apxs + +Once you have configured the source code by running ``configure``, you +can build mod_wsgi using the command:: + + make + +This will compile the mod_wsgi source code and produce a single +``mod_wsgi.so`` file which then needs to be installed into a common +location so that the Apache httpd server can use it. + +Installing the mod_wsgi module +------------------------------ + +What you need to do to install the mod_wsgi module depends on which version +of MacOS X you are using. + +For the Apache httpd server provided by the operating system, the directory +``/usr/libexec/apache2`` is used to store the compiled modules. Prior to +MacOS X El Capitan (10.11) this directory was writable and the mod_wsgi +module could be installed here along with all the default modules. With the +introduction of the System Integrity Protection (SIP_) feature in MacOS X +El Capitan this directory is not writable, not even to the root user. + +Because of this, if you are using a version of MacOS X prior to MacOS X El +Capitan (10.11) you can use the command:: + + sudo make install + +to install the mod_wsgi module. As ``sudo`` is being run, you will be +prompted for your password. The module will be installed into the +directory ``/usr/libexec/apache2``. Within the Apache httpd server +configuration file you can then use the standard ``LoadModule`` line +of:: + + LoadModule wsgi_module libexec/apache2/mod_wsgi.so + +If however you are using MacOS X El Capitan (10.11) or later, the mod_wsgi +module will need to be installed into a different location. If you don't +and try to run just ``sudo make install``, it will fail with the output:: + + ./apxs -i -S LIBEXECDIR=/usr/libexec/apache2 -n 'mod_wsgi' src/server/mod_wsgi.la + /usr/share/httpd/build/instdso.sh SH_LIBTOOL='./libtool' src/server/mod_wsgi.la /usr/libexec/apache2 + ./libtool --mode=install install src/server/mod_wsgi.la /usr/libexec/apache2/ + libtool: install: install src/server/.libs/mod_wsgi.so /usr/libexec/apache2/mod_wsgi.so + install: /usr/libexec/apache2/mod_wsgi.so: Operation not permitted + apxs:Error: Command failed with rc=4653056 + . + make: *** [install] Error 1 + +The directory you use to install the mod_wsgi module is up to you, but +one suggested option is that you use the directory +``/usr/local/httpd/modules``. Just ensure that this isn't already used +by a separate installation of the Apache httpd server. + +To install the mod_wsgi module into this directory use the command:: + + sudo make install LIBEXECDIR=/usr/local/httpd/modules + +The output from the command will be similar to:: + + mkdir -p /usr/local/httpd/modules + ./apxs -i -S LIBEXECDIR=/usr/local/httpd/modules -n 'mod_wsgi' src/server/mod_wsgi.la + /usr/share/httpd/build/instdso.sh SH_LIBTOOL='./libtool' src/server/mod_wsgi.la /usr/local/httpd/modules + ./libtool --mode=install install src/server/mod_wsgi.la /usr/local/httpd/modules/ + libtool: install: install src/server/.libs/mod_wsgi.so /usr/local/httpd/modules/mod_wsgi.so + libtool: install: install src/server/.libs/mod_wsgi.lai /usr/local/httpd/modules/mod_wsgi.la + libtool: install: install src/server/.libs/mod_wsgi.a /usr/local/httpd/modules/mod_wsgi.a + libtool: install: chmod 644 /usr/local/httpd/modules/mod_wsgi.a + libtool: install: ranlib /usr/local/httpd/modules/mod_wsgi.a + libtool: install: warning: remember to run `libtool --finish /usr/libexec/apache2' + chmod 755 /usr/local/httpd/modules/mod_wsgi.so + +The warning about needing to run ``libtool --finish`` can be ignored as it +is not required for everything to work. + +With the mod_wsgi module installed in this location, the ``LoadModule`` line +in the Apache httpd configuration file should be:: + + LoadModule wsgi_module /usr/local/httpd/modules/mod_wsgi.so + +Normal steps to then configure the Apache httpd server and mod_wsgi for +your specific WSGI application would then be followed. + +.. _SIP: https://en.wikipedia.org/wiki/System_Integrity_Protection diff --git a/src/server/__init__.py b/src/server/__init__.py index 8929434..a182bd0 100644 --- a/src/server/__init__.py +++ b/src/server/__init__.py @@ -394,6 +394,10 @@ ErrorLog "%(error_log_file)s" </IfDefine> LogLevel %(log_level)s +<IfDefine MOD_WSGI_ERROR_LOG_FORMAT> +ErrorLogFormat "%(error_log_format)s" +</IfDefine> + <IfDefine MOD_WSGI_ACCESS_LOG> <IfModule !log_config_module> LoadModule log_config_module ${MOD_WSGI_MODULES_DIRECTORY}/mod_log_config.so @@ -2257,6 +2261,8 @@ option_list = ( optparse.make_option('--access-log-format', metavar='FORMAT', help='Specify the format of the access log records.'), + optparse.make_option('--error-log-format', metavar='FORMAT', + help='Specify the format of the error log records.'), optparse.make_option('--error-log-name', metavar='FILE-NAME', default='error_log', help='Specify the name of the error ' @@ -2646,6 +2652,10 @@ def _cmd_setup_server(command, args, options): options['access_log_format'] = options['access_log_format'].replace( '\"', '\\"') + if options['error_log_format']: + options['error_log_format'] = options['error_log_format'].replace( + '\"', '\\"') + options['pid_file'] = ((options['pid_file'] and os.path.abspath( options['pid_file'])) or os.path.join(options['server_root'], 'httpd.pid')) @@ -3006,6 +3016,8 @@ def _cmd_setup_server(command, args, options): options['httpd_arguments_list'].append('-DMOD_WSGI_DIRECTORY_INDEX') if options['directory_listing']: options['httpd_arguments_list'].append('-DMOD_WSGI_DIRECTORY_LISTING') + if options['error_log_format']: + options['httpd_arguments_list'].append('-DMOD_WSGI_ERROR_LOG_FORMAT') if options['access_log']: options['httpd_arguments_list'].append('-DMOD_WSGI_ACCESS_LOG') if options['rotate_logs']: diff --git a/src/server/mod_wsgi.c b/src/server/mod_wsgi.c index 66ecb88..d684dd7 100644 --- a/src/server/mod_wsgi.c +++ b/src/server/mod_wsgi.c @@ -3749,6 +3749,8 @@ static int wsgi_execute_script(request_rec *r) int status; + WSGIThreadInfo *thread_info = NULL; + /* Grab request configuration. */ config = (WSGIRequestConfig *)ap_get_module_config(r->request_config, @@ -3958,7 +3960,7 @@ static int wsgi_execute_script(request_rec *r) /* Setup metrics for start of request. */ - wsgi_start_request(); + thread_info = wsgi_start_request(); /* Load module if not already loaded. */ @@ -3995,6 +3997,9 @@ static int wsgi_execute_script(request_rec *r) PyObject *method = NULL; PyObject *args = NULL; + Py_INCREF(adapter->log); + thread_info->log = adapter->log; + Py_INCREF(object); status = Adapter_run(adapter, object); Py_DECREF(object); @@ -4029,6 +4034,8 @@ static int wsgi_execute_script(request_rec *r) Py_XDECREF(object); Py_XDECREF(method); + Py_CLEAR(thread_info->log); + adapter->bb = NULL; } @@ -6053,6 +6060,27 @@ static void wsgi_build_environment(request_rec *r) apr_table_setn(r->subprocess_env, "mod_wsgi.request_start", apr_psprintf(r->pool, "%" APR_TIME_T_FMT, r->request_time)); + +#if AP_MODULE_MAGIC_AT_LEAST(20100923,2) + if (!r->log_id || !r->connection->log_id) { + const char **id; + + /* Need to cast const away. */ + + if (r) + id = &((request_rec *)r)->log_id; + else + id = &((conn_rec *)c)->log_id; + + ap_run_generate_log_id(c, r, id); + } + + if (r->log_id) + apr_table_setn(r->subprocess_env, "mod_wsgi.request_id", r->log_id); + if (r->connection->log_id) + apr_table_setn(r->subprocess_env, "mod_wsgi.connection_id", + r->connection->log_id); +#endif } typedef struct { @@ -12325,9 +12353,13 @@ static int wsgi_hook_daemon_handler(conn_rec *c) #if AP_MODULE_MAGIC_AT_LEAST(20111130,0) r->connection->client_ip = (char *)apr_table_get(r->subprocess_env, "REMOTE_ADDR"); + r->connection->client_addr->port = atoi(apr_table_get(r->subprocess_env, + "REMOTE_PORT")); #else r->connection->remote_ip = (char *)apr_table_get(r->subprocess_env, "REMOTE_ADDR"); + r->connection->remote_addr->port = atoi(apr_table_get(r->subprocess_env, + "REMOTE_PORT")); #endif #if AP_MODULE_MAGIC_AT_LEAST(20111130,0) @@ -12428,7 +12460,9 @@ static int wsgi_hook_daemon_handler(conn_rec *c) errno = 0; config->request_start = apr_strtoi64(item, (char **)&item, 10); - if (!(!*item && errno != ERANGE)) + if (!*item && errno != ERANGE) + r->request_time = config->request_start; + else config->request_start = 0.0; } @@ -12448,6 +12482,18 @@ static int wsgi_hook_daemon_handler(conn_rec *c) apr_psprintf(r->pool, "%" APR_TIME_T_FMT, config->daemon_start)); +#if AP_MODULE_MAGIC_AT_LEAST(20100923,2) + item = apr_table_get(r->subprocess_env, "mod_wsgi.request_id"); + + if (item) + r->log_id = item; + + item = apr_table_get(r->subprocess_env, "mod_wsgi.connection_id"); + + if (item) + r->connection->log_id = item; +#endif + /* * Install the standard HTTP input filter and set header for * chunked transfer encoding to force it to dechunk the input. diff --git a/src/server/wsgi_logger.c b/src/server/wsgi_logger.c index ab42c21..b15b066 100644 --- a/src/server/wsgi_logger.c +++ b/src/server/wsgi_logger.c @@ -22,6 +22,7 @@ #include "wsgi_server.h" #include "wsgi_metrics.h" +#include "wsgi_thread.h" /* ------------------------------------------------------------------------- */ @@ -220,6 +221,13 @@ static void Log_dealloc(LogObject *self) static PyObject *Log_flush(LogObject *self, PyObject *args) { + WSGIThreadInfo *thread_info = NULL; + + thread_info = wsgi_thread_info(0, 0); + + if (thread_info && thread_info->log && thread_info->log != (PyObject *)self) + return Log_flush((LogObject *)thread_info->log, args); + if (self->expired) { PyErr_SetString(PyExc_RuntimeError, "log object has expired"); return NULL; @@ -241,6 +249,13 @@ static PyObject *Log_close(LogObject *self, PyObject *args) { PyObject *result = NULL; + WSGIThreadInfo *thread_info = NULL; + + thread_info = wsgi_thread_info(0, 0); + + if (thread_info && thread_info->log && thread_info->log != (PyObject *)self) + return Log_close((LogObject *)thread_info->log, args); + if (!self->expired) result = Log_flush(self, args); @@ -368,6 +383,13 @@ static PyObject *Log_write(LogObject *self, PyObject *args) const char *msg = NULL; int len = -1; + WSGIThreadInfo *thread_info = NULL; + + thread_info = wsgi_thread_info(0, 0); + + if (thread_info && thread_info->log && thread_info->log != (PyObject *)self) + return Log_write((LogObject *)thread_info->log, args); + if (self->expired) { PyErr_SetString(PyExc_RuntimeError, "log object has expired"); return NULL; @@ -388,6 +410,13 @@ static PyObject *Log_writelines(LogObject *self, PyObject *args) PyObject *iterator = NULL; PyObject *item = NULL; + WSGIThreadInfo *thread_info = NULL; + + thread_info = wsgi_thread_info(0, 0); + + if (thread_info && thread_info->log && thread_info->log != (PyObject *)self) + return Log_writelines((LogObject *)thread_info->log, args); + if (self->expired) { PyErr_SetString(PyExc_RuntimeError, "log object has expired"); return NULL; @@ -469,6 +498,13 @@ static PyObject *Log_closed(LogObject *self, void *closure) #if PY_MAJOR_VERSION < 3 static PyObject *Log_get_softspace(LogObject *self, void *closure) { + WSGIThreadInfo *thread_info = NULL; + + thread_info = wsgi_thread_info(0, 0); + + if (thread_info && thread_info->log && thread_info->log != (PyObject *)self) + return Log_get_softspace((LogObject *)thread_info->log, closure); + return PyInt_FromLong(self->softspace); } @@ -476,6 +512,13 @@ static int Log_set_softspace(LogObject *self, PyObject *value) { long new; + WSGIThreadInfo *thread_info = NULL; + + thread_info = wsgi_thread_info(0, 0); + + if (thread_info && thread_info->log && thread_info->log != (PyObject *)self) + return Log_set_softspace((LogObject *)thread_info->log, value); + if (value == NULL) { PyErr_SetString(PyExc_TypeError, "can't delete softspace attribute"); return -1; @@ -494,6 +537,13 @@ static int Log_set_softspace(LogObject *self, PyObject *value) static PyObject *Log_get_encoding(LogObject *self, void *closure) { + WSGIThreadInfo *thread_info = NULL; + + thread_info = wsgi_thread_info(0, 0); + + if (thread_info && thread_info->log && thread_info->log != (PyObject *)self) + return Log_get_encoding((LogObject *)thread_info->log, closure); + return PyUnicode_FromString("utf-8"); } diff --git a/src/server/wsgi_metrics.c b/src/server/wsgi_metrics.c index 696b22b..9f4558a 100644 --- a/src/server/wsgi_metrics.c +++ b/src/server/wsgi_metrics.c @@ -77,7 +77,7 @@ static double wsgi_utilization_time(int adjustment) return utilization; } -void wsgi_start_request(void) +WSGIThreadInfo *wsgi_start_request(void) { WSGIThreadInfo *thread_info; @@ -85,6 +85,8 @@ void wsgi_start_request(void) thread_info->request_data = PyDict_New(); wsgi_utilization_time(1); + + return thread_info; } void wsgi_end_request(void) @@ -93,8 +95,13 @@ void wsgi_end_request(void) thread_info = wsgi_thread_info(0, 1); - if (thread_info && thread_info->request_data) - Py_CLEAR(thread_info->request_data); + if (thread_info) { + if (thread_info->log) + Py_CLEAR(thread_info->log); + + if (thread_info->request_data) + Py_CLEAR(thread_info->request_data); + } wsgi_utilization_time(-1); } diff --git a/src/server/wsgi_metrics.h b/src/server/wsgi_metrics.h index 60b46a3..801d914 100644 --- a/src/server/wsgi_metrics.h +++ b/src/server/wsgi_metrics.h @@ -24,6 +24,8 @@ #include "wsgi_python.h" #include "wsgi_apache.h" +#include "wsgi_thread.h" + /* ------------------------------------------------------------------------- */ extern apr_uint64_t wsgi_total_requests; @@ -34,7 +36,7 @@ extern apr_thread_mutex_t* wsgi_monitor_lock; extern PyMethodDef wsgi_process_metrics_method[]; -extern void wsgi_start_request(void); +extern WSGIThreadInfo *wsgi_start_request(void); extern void wsgi_end_request(void); extern PyMethodDef wsgi_server_metrics_method[]; diff --git a/src/server/wsgi_thread.c b/src/server/wsgi_thread.c index 1111491..8e7c640 100644 --- a/src/server/wsgi_thread.c +++ b/src/server/wsgi_thread.c @@ -57,6 +57,8 @@ WSGIThreadInfo *wsgi_thread_info(int create, int request) thread_handle = (WSGIThreadInfo *)apr_pcalloc( wsgi_server->process->pool, sizeof(WSGIThreadInfo)); + thread_handle->log = NULL; + thread_handle->thread_id = wsgi_total_threads++; entry = (WSGIThreadInfo **)apr_array_push(wsgi_thread_details); diff --git a/src/server/wsgi_thread.h b/src/server/wsgi_thread.h index 66a3454..cddd127 100644 --- a/src/server/wsgi_thread.h +++ b/src/server/wsgi_thread.h @@ -31,6 +31,7 @@ typedef struct { int request_thread; apr_int64_t request_count; PyObject *request_data; + PyObject *log; } WSGIThreadInfo; extern int wsgi_total_threads; diff --git a/src/server/wsgi_version.h b/src/server/wsgi_version.h index 894d77f..e313095 100644 --- a/src/server/wsgi_version.h +++ b/src/server/wsgi_version.h @@ -25,8 +25,8 @@ #define MOD_WSGI_MAJORVERSION_NUMBER 4 #define MOD_WSGI_MINORVERSION_NUMBER 5 -#define MOD_WSGI_MICROVERSION_NUMBER 3 -#define MOD_WSGI_VERSION_STRING "4.5.3" +#define MOD_WSGI_MICROVERSION_NUMBER 4 +#define MOD_WSGI_VERSION_STRING "4.5.4" /* ------------------------------------------------------------------------- */ diff --git a/tests/environ.wsgi b/tests/environ.wsgi index 55415fd..59694eb 100644 --- a/tests/environ.wsgi +++ b/tests/environ.wsgi @@ -13,6 +13,10 @@ import mod_wsgi import apache def application(environ, start_response): + print('global message') + print('request message', file=environ['wsgi.errors']) + print('queued message', end='') + headers = [] headers.append(('Content-Type', 'text/plain; charset="UTF-8"')) write = start_response('200 OK', headers) |
