From 10836dc90207a9f728d5c6f83ceb7cf64b047f42 Mon Sep 17 00:00:00 2001 From: David Strauss Date: Tue, 26 Apr 2016 17:50:44 -0700 Subject: [PATCH] Initial HTTP(S) driver. --- README.md | 4 +- apm.c | 25 +++++++++ apm.ini | 23 ++++++++ config.m4 | 37 ++++++++++++- driver_http.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++ driver_http.h | 30 +++++++++++ php_apm.h | 21 ++++++++ 7 files changed, 280 insertions(+), 3 deletions(-) create mode 100644 driver_http.c create mode 100644 driver_http.h diff --git a/README.md b/README.md index 9a53b52..b41f5b1 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ It doesn't require any modification to your application's code and let's you col 1. `$ git clone https://github.com/patrickallaert/php-apm.git` 2. `$ cd php-apm` 3. `$ phpize` -4. Configure the extension, by default, **sqlite3**, **MariaDB/MySQL**, **[StatsD](https://github.com/etsy/statsd/)** and **Socket** support are enabled: +4. Configure the extension, by default, **sqlite3**, **MariaDB/MySQL**, **[StatsD](https://github.com/etsy/statsd/)**, **HTTP**, and **Socket** support are enabled: ``` - $ ./configure [--with-sqlite3[=DIR]] [--with-mysql[=DIR]] [--enable-statsd] [--enable-socket] [--with-debugfile[=FILE]] + $ ./configure [--with-sqlite3[=DIR]] [--with-mysql[=DIR]] [--enable-statsd] [--enable-socket] [--enable-http] [--with-debugfile[=FILE]] ``` To disable the support of a `--with-*` switch, use: `--without-*`, example: `$ ./configure --without-sqlite3` To disable the support of a `--enable-*` switch, use: `--disable-*`, example: `$ ./configure --disable-socket` diff --git a/apm.c b/apm.c index 624369c..a5a51d9 100644 --- a/apm.c +++ b/apm.c @@ -53,6 +53,9 @@ #ifdef APM_DRIVER_SOCKET # include "driver_socket.h" #endif +#ifdef APM_DRIVER_HTTP + #include "driver_http.h" +#endif ZEND_DECLARE_MODULE_GLOBALS(apm); static PHP_GINIT_FUNCTION(apm); @@ -240,6 +243,25 @@ PHP_INI_BEGIN() /* process silenced events? */ STD_PHP_INI_BOOLEAN("apm.socket_process_silenced_events", "1", PHP_INI_PERDIR, OnUpdateBool, socket_process_silenced_events, zend_apm_globals, apm_globals) #endif + +#ifdef APM_DRIVER_HTTP + /* Boolean controlling whether the driver is active or not */ + STD_PHP_INI_BOOLEAN("apm.http_enabled", "1", PHP_INI_ALL, OnUpdateBool, http_enabled, zend_apm_globals, apm_globals) + /* Boolean controlling the collection of stats */ + STD_PHP_INI_BOOLEAN("apm.http_stats_enabled", "1", PHP_INI_ALL, OnUpdateBool, http_stats_enabled, zend_apm_globals, apm_globals) + /* Control which exceptions to collect (0: none exceptions collected, 1: collect uncaught exceptions (default), 2: collect ALL exceptions) */ + STD_PHP_INI_ENTRY("apm.http_exception_mode","1", PHP_INI_PERDIR, OnUpdateLongGEZero, http_exception_mode, zend_apm_globals, apm_globals) + /* error_reporting of the driver */ + STD_PHP_INI_ENTRY("apm.http_error_reporting", NULL, PHP_INI_ALL, OnUpdateAPMhttpErrorReporting, http_error_reporting, zend_apm_globals, apm_globals) + /* process silenced events? */ + STD_PHP_INI_BOOLEAN("apm.http_process_silenced_events", "1", PHP_INI_PERDIR, OnUpdateBool, http_process_silenced_events, zend_apm_globals, apm_globals) + STD_PHP_INI_ENTRY("apm.http_request_timeout", "1000", PHP_INI_ALL, OnUpdateLong, http_request_timeout, zend_apm_globals, apm_globals) + STD_PHP_INI_ENTRY("apm.http_server", "http://localhost", PHP_INI_ALL, OnUpdateString, http_server, zend_apm_globals, apm_globals) + STD_PHP_INI_ENTRY("apm.http_client_certificate", NULL, PHP_INI_ALL, OnUpdateString, http_client_certificate, zend_apm_globals, apm_globals) + STD_PHP_INI_ENTRY("apm.http_client_key", NULL, PHP_INI_ALL, OnUpdateString, http_client_key, zend_apm_globals, apm_globals) + STD_PHP_INI_ENTRY("apm.http_certificate_authorities", NULL, PHP_INI_ALL, OnUpdateString, http_certificate_authorities, zend_apm_globals, apm_globals) + STD_PHP_INI_ENTRY("apm.http_max_backtrace_length", "0", PHP_INI_ALL, OnUpdateLong, http_max_backtrace_length, zend_apm_globals, apm_globals) +#endif PHP_INI_END() static PHP_GINIT_FUNCTION(apm) @@ -272,6 +294,9 @@ static PHP_GINIT_FUNCTION(apm) *next = apm_driver_socket_create(); next = &(*next)->next; #endif +#ifdef APM_DRIVER_HTTP + *next = apm_driver_http_create(); +#endif } static void recursive_free_driver(apm_driver_entry **driver) diff --git a/apm.ini b/apm.ini index 055884d..90826ae 100644 --- a/apm.ini +++ b/apm.ini @@ -97,3 +97,26 @@ extension=apm.so ; Socket path (accept multiple entries, separated by "|", prefixed with "file:" or "tcp:") ; Example: apm.socket_path=file:/var/tmp/apm.sock|tcp:localhost:1234 ; apm.socket_path=file:/tmp/apm.sock + +; HTTP configuration +; Whether to enable the HTTP driver +; apm.http_enabled=On +; Whether to collect stats for the HTTP driver +; apm.http_stats_enabled=On +; Error reporting level specific to the HTTP driver +; apm.http_error_reporting=E_ALL|E_STRICT +; Control which exceptions to collect (0: none exceptions collected, 1: collect uncaught exceptions (default), 2: collect ALL exceptions) +; apm.http_exception_mode=1 +; Stores silenced events +; apm.http_process_silenced_events = On +; Server for POSTing events +; apm.http_server=http://localhost +; Client and certificate authority for making the server connection +; apm.http_certificate_authorities="" +; apm.http_client_certificate="" +; apm.http_client_key="" +; Maximum number of characters to include in backtrace (0=unlimited) +; apm.http_max_backtrace_length=0 +; Timeout (in ms) for POSTing events +; apm.http_request_timeout=1000 + diff --git a/config.m4 b/config.m4 index 647bcaa..f89a54d 100644 --- a/config.m4 +++ b/config.m4 @@ -35,6 +35,8 @@ PHP_ARG_ENABLE(statsd, enable support for statsd, [ --enable-statsd Enable statsd support], yes, no) PHP_ARG_ENABLE(socket, enable support for socket, [ --enable-socket Enable socket support], yes, no) +PHP_ARG_ENABLE(http, enable support for http, +[ --enable-http Enable HTTP support], yes, no) PHP_ARG_WITH(debugfile, enable the debug file, [ --with-debugfile=[FILE] Location of debugging file (/tmp/apm.debug by default)], no, no) PHP_ARG_WITH(defaultdb, set default sqlite3 default DB path, @@ -199,6 +201,39 @@ if test "$PHP_APM" != "no"; then AC_DEFINE(APM_DRIVER_SOCKET, 1, [activate socket driver]) fi - PHP_NEW_EXTENSION(apm, apm.c backtrace.c $sqlite3_driver $mysql_driver $statsd_driver $socket_driver, $ext_shared) + if test "$PHP_HTTP" != "no"; then + http_driver="driver_http.c" + AC_DEFINE(APM_DRIVER_HTTP, 1, [activate HTTP sending driver]) + AC_DEFINE(HAVE_HTTP, 1, [HTTP found and included]) + + if test -r $PHP_CURL/include/curl/easy.h; then + CURL_DIR=$PHP_CURL + else + AC_MSG_CHECKING(for cURL in default path) + for i in /usr/local /usr; do + if test -r $i/include/curl/easy.h; then + CURL_DIR=$i + AC_MSG_RESULT(found in $i) + break + fi + done + fi + + PHP_ADD_INCLUDE($CURL_DIR/include) + PHP_EVAL_LIBLINE($CURL_LIBS, APM_SHARED_LIBADD) + PHP_ADD_LIBRARY_WITH_PATH(curl, $CURL_DIR/$PHP_LIBDIR, APM_SHARED_LIBADD) + + PHP_CHECK_LIBRARY(curl,curl_easy_perform, + [ + AC_DEFINE(HAVE_CURL,1,[ ]) + ],[ + AC_MSG_ERROR(There is something wrong. Please check config.log for more information.) + ],[ + $CURL_LIBS -L$CURL_DIR/$PHP_LIBDIR + ]) + + fi + + PHP_NEW_EXTENSION(apm, apm.c backtrace.c $sqlite3_driver $mysql_driver $statsd_driver $socket_driver $http_driver, $ext_shared) PHP_SUBST(APM_SHARED_LIBADD) fi diff --git a/driver_http.c b/driver_http.c new file mode 100644 index 0000000..dff2379 --- /dev/null +++ b/driver_http.c @@ -0,0 +1,143 @@ +/* + +----------------------------------------------------------------------+ + | APM stands for Alternative PHP Monitor | + +----------------------------------------------------------------------+ + | Copyright (c) 2011-2016 David Strauss | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: David Strauss | + +----------------------------------------------------------------------+ +*/ + +#include +#include +#include "php_apm.h" +#include "php_ini.h" + +#include "driver_http.h" + +ZEND_EXTERN_MODULE_GLOBALS(apm) + +APM_DRIVER_CREATE(http) + +char *truncate_data(char *input_str, size_t max_len) +{ + char *truncated; + input_str = input_str ? input_str : NULL; + if (max_len == 0) + return strdup(input_str); + truncated = strndup(input_str, max_len); + return truncated; +} + +/* Insert an event in the backend */ +void apm_driver_http_process_event(PROCESS_EVENT_ARGS) +{ + CURL *curl; + CURLcode res; + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + if(curl) { + struct curl_httppost *formpost = NULL; + struct curl_httppost *lastptr = NULL; + struct curl_slist *headerlist = NULL; + static const char buf[] = "Expect:"; + char int2string[64]; + char *trace_to_send; + size_t max_len = 0; + + if (APM_G(http_max_backtrace_length) >= 0) + max_len = APM_G(http_max_backtrace_length); + + trace_to_send = truncate_data(trace, max_len); + + sprintf(int2string, "%d", type); + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "type", + CURLFORM_COPYCONTENTS, int2string, + CURLFORM_END); + + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "file", + CURLFORM_COPYCONTENTS, error_filename ? error_filename : "", + CURLFORM_END); + + sprintf(int2string, "%d", error_lineno); + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "line", + CURLFORM_COPYCONTENTS, int2string, + CURLFORM_END); + + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "message", + CURLFORM_COPYCONTENTS, msg ? msg : "", + CURLFORM_END); + + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "backtrace", + CURLFORM_COPYCONTENTS, trace_to_send, + CURLFORM_END); + + headerlist = curl_slist_append(headerlist, buf); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); + curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); + + curl_easy_setopt(curl, CURLOPT_URL, APM_G(http_server)); + curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, APM_G(http_request_timeout)); + if (APM_G(http_client_certificate) != NULL) { + curl_easy_setopt(curl, CURLOPT_SSLCERT, APM_G(http_client_certificate)); + } + if (APM_G(http_client_key) != NULL) { + curl_easy_setopt(curl, CURLOPT_SSLKEY, APM_G(http_client_key)); + } + if (APM_G(http_certificate_authorities) != NULL) { + curl_easy_setopt(curl, CURLOPT_CAINFO, APM_G(http_certificate_authorities)); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + } + + res = curl_easy_perform(curl); + + APM_DEBUG("[HTTP driver] Result: %s\n", curl_easy_strerror(res)); + + /* Always clean up. */ + curl_easy_cleanup(curl); + free(trace_to_send); + } +} + +int apm_driver_http_minit(int module_number) +{ + return SUCCESS; +} + +int apm_driver_http_rinit() +{ + return SUCCESS; +} + +int apm_driver_http_mshutdown() +{ + return SUCCESS; +} + +int apm_driver_http_rshutdown() +{ + return SUCCESS; +} + +void apm_driver_http_process_stats(TSRMLS_D) +{ +} diff --git a/driver_http.h b/driver_http.h new file mode 100644 index 0000000..3bed20c --- /dev/null +++ b/driver_http.h @@ -0,0 +1,30 @@ +/* + +----------------------------------------------------------------------+ + | APM stands for Alternative PHP Monitor | + +----------------------------------------------------------------------+ + | Copyright (c) 2011-2016 David Strauss | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: David Strauss | + +----------------------------------------------------------------------+ +*/ + +#ifndef DRIVER_HTTP_H +#define DRIVER_HTTP_H + +#include "zend_API.h" + +#define APM_E_http APM_E_ALL + +apm_driver_entry * apm_driver_http_create(); + +PHP_INI_MH(OnUpdateAPMhttpErrorReporting); + +#endif diff --git a/php_apm.h b/php_apm.h index 4a5ee24..9758d96 100644 --- a/php_apm.h +++ b/php_apm.h @@ -339,6 +339,27 @@ ZEND_BEGIN_MODULE_GLOBALS(apm) apm_event_entry *socket_events; apm_event_entry **socket_last_event; #endif + +#ifdef APM_DRIVER_HTTP + /* Boolean controlling whether the driver is active or not */ + zend_bool http_enabled; + /* Boolean controlling the collection of stats */ + zend_bool http_stats_enabled; + /* (unused for HTTP) */ + long http_exception_mode; + /* (unused for HTTP) */ + int http_error_reporting; + /* Option to process silenced events */ + zend_bool http_process_silenced_events; + + long http_request_timeout; + char *http_server; + char *http_client_certificate; + char *http_client_key; + char *http_certificate_authorities; + long http_max_backtrace_length; +#endif + ZEND_END_MODULE_GLOBALS(apm) #ifdef ZTS