summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG17
-rw-r--r--INSTALL4
-rw-r--r--mod_log_sql.c195
3 files changed, 155 insertions, 61 deletions
diff --git a/CHANGELOG b/CHANGELOG
index b0da0a2..34aed54 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,14 +1,27 @@
1$Id: CHANGELOG,v 1.6 2002/02/24 20:17:16 helios Exp $ 1$Id: CHANGELOG,v 1.7 2002/04/02 20:19:30 helios Exp $
2 2
3 3
4TODO: 4TODO:
5* Full commenting of the code. 5* Full commenting of the code.
6* Rethink documentation flow and rewrite? 6* Rethink documentation flow and rewrite?
7* Message queueing during server downtime? 7* Config directives for proxy clients
8* Port connection portion to other DBMS? Genericize the module?
8 9
9 10
10CHANGES: 11CHANGES:
11 12
131.15:
14* Vastly improved error reporting is a lot clearer about lost db
15 connections, etc. Some unreachable code has been corrected.
16* The way that query-retries and openlink-retries are handled has
17 been tweaked and improved.
18* Missed database entries are now preserved for later inclusion. This
19 file is not held open but is closed after each use, so it's safe
20 to delete while Apache is running.
21* Now each child instantiates its MySQL link upon birth rather than
22 waiting for its first request.
23
24
121.14: 251.14:
13* Improved the apxs instructions based on user feedback, including the 26* Improved the apxs instructions based on user feedback, including the
14 mysql.sock define issue. 27 mysql.sock define issue.
diff --git a/INSTALL b/INSTALL
index 0391e88..3226e89 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,4 +1,4 @@
1$Id: INSTALL,v 1.5 2002/02/24 20:17:16 helios Exp $ 1$Id: INSTALL,v 1.6 2002/04/02 20:19:30 helios Exp $
2 2
3 3
4Requirements 4Requirements
@@ -126,7 +126,7 @@ Installation (as a static module compiled into httpd)
126 6a) Edit Configuration.apaci as follows... 126 6a) Edit Configuration.apaci as follows...
127 127
128 * Append the following string to the EXTRA_LIBS= line. (/usr/lib/mysql is where your libmysqlclient.a file lives): 128 * Append the following string to the EXTRA_LIBS= line. (/usr/lib/mysql is where your libmysqlclient.a file lives):
129 -L/usr/lib/mysql -lmysqlclient -lm 129 -L/usr/lib/mysql -lmysqlclient -lm -lz
130 130
131 * Add this line at the end of the file: 131 * Add this line at the end of the file:
132 Module mysql_log_module mod_log_mysql.o 132 Module mysql_log_module mod_log_mysql.o
diff --git a/mod_log_sql.c b/mod_log_sql.c
index 6780b74..61c1ed9 100644
--- a/mod_log_sql.c
+++ b/mod_log_sql.c
@@ -1,10 +1,13 @@
1/* $Id: mod_log_sql.c,v 1.5 2002/01/15 18:40:14 helios Exp $ */ 1/* $Id: mod_log_sql.c,v 1.6 2002/04/02 20:19:30 helios Exp $ */
2 2
3 3
4/* DEFINES */ 4/* DEFINES */
5#define MYSQL_ERROR(mysql) ((mysql)?(mysql_error(mysql)):"MySQL server has gone away") 5#define MYSQL_ERROR(mysql) ((mysql)?(mysql_error(mysql)):"MySQL server has gone away")
6#define ERRLEVEL APLOG_ERR|APLOG_NOERRNO 6#define ERRLEVEL APLOG_ERR|APLOG_NOERRNO
7#define WARNINGLEVEL APLOG_WARNING|APLOG_NOERRNO
8#define NOTICELEVEL APLOG_NOTICE|APLOG_NOERRNO
7#define DEBUGLEVEL APLOG_INFO|APLOG_NOERRNO 9#define DEBUGLEVEL APLOG_INFO|APLOG_NOERRNO
10#define PRESERVEFILE "/tmp/mysql-preserve"
8/* (MYSQLSOCKET, DEBUG and WANT_SSL_LOGGING are defined in the Makefile DEFS line.) */ 11/* (MYSQLSOCKET, DEBUG and WANT_SSL_LOGGING are defined in the Makefile DEFS line.) */
9 12
10 13
@@ -463,15 +466,35 @@ const char *mysql_escape_log(const char *str, pool *p)
463 } 466 }
464} 467}
465 468
466void open_logdb_link() 469int open_logdb_link()
467{ 470{
468 if (mysql_log != NULL) { /* virtual database link shared with main server */ 471 if (mysql_log != NULL) {
469 return; 472 return 2;
470 } 473 }
471 if (db_name) { /* open an SQL link */ 474
475 if (db_name) {
472 mysql_init(&sql_server); 476 mysql_init(&sql_server);
473 mysql_log = mysql_real_connect(&sql_server, db_host, db_user, db_pwd, db_name, 0, MYSQLSOCKET, 0); 477 mysql_log = mysql_real_connect(&sql_server, db_host, db_user, db_pwd, db_name, 0, MYSQLSOCKET, 0);
478
479 if (mysql_log != NULL) {
480 return 1;
481 } else {
482 return 0;
483 }
474 } 484 }
485 return 0;
486}
487
488void preserve_entry(request_rec *r, const char *query)
489{
490 FILE *fp;
491
492 fp = fopen(PRESERVEFILE, "a");
493 if (fp == NULL)
494 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"MySQL: attempted append of local offline file but failed.");
495 else
496 fprintf(fp,"%s\n", query);
497 fclose(fp);
475} 498}
476 499
477int safe_mysql_query(request_rec *r, const char *query) 500int safe_mysql_query(request_rec *r, const char *query)
@@ -481,57 +504,61 @@ int safe_mysql_query(request_rec *r, const char *query)
481 int ret; 504 int ret;
482 char *str; 505 char *str;
483 void (*handler) (int); 506 void (*handler) (int);
507
484 508
485 /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */ 509 /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */
486 handler = signal(SIGPIPE, SIG_IGN); 510 handler = signal(SIGPIPE, SIG_IGN);
487 511
488 /* If there's no DB link, or if we run the query and it gacks, try to be graceful */ 512 /* First attempt for the query */
489 if ( !mysql_log || 513 retval = mysql_query(mysql_log, query);
490 ( 514
491 (retval = mysql_query(mysql_log, query)) && 515 /* If we ran the query and it returned an error, try to be graceful.
492 (mysql_errno(mysql_log) != 0) 516 * We only reach this point if the module thinks it has a valid connection to the
493 ) 517 * database (i.e. mysql_log is non-null). But that connection could go sour
494 ) 518 * at any time, hence the check. */
495 519 if ( retval != 0 )
496 { /* We need to restart the server link */ 520 {
497 mysql_log = NULL; 521 /* Something went wrong, so start by trying to restart the db link. */
498 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"MySQL: connection lost, attempting reconnect"); 522 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"MySQL: attempting reconnect because API said: %s", MYSQL_ERROR(mysql_log));
499 523
524 mysql_log = NULL;
500 open_logdb_link(); 525 open_logdb_link();
501 526
502 if (mysql_log == NULL) { /* still unable to link */ 527 if (mysql_log == NULL) { /* still unable to link */
503 signal(SIGPIPE, handler); 528 signal(SIGPIPE, handler);
504 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"MySQL: reconnect failed"); 529 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"MySQL: httpd child reconnect failed, unable to reach database. SQL logging stopped until an httpd child regains a db connection.");
530 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"MySQL: log entries are being preserved in %s",PRESERVEFILE);
505 return retval; 531 return retval;
506 } 532 } else {
533 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"MySQL: reconnect successful.");
534 }
507 535
508 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"MySQL: reconnect successful"); 536 /* Attempt a single re-try... First sleep for a tiny amount of time. */
509 retval = mysql_query(mysql_log, query); 537 delay.tv_sec = 0;
538 delay.tv_nsec = 500000000; /* max is 999999999 (nine nines) */
539 ret = nanosleep(&delay, &remainder);
540 if (ret && errno != EINTR)
541 perror("nanosleep");
542
543 /* Now make our second attempt */
544 retval = mysql_query(mysql_log,query);
545
546 /* If this one also failed, log that and append to our local offline file */
547 if ( retval != 0 )
548 {
549 str = pstrcat(r->pool, "MySQL delayed insert attempt failed, API said: ", MYSQL_ERROR(mysql_log), NULL);
550 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,str);
551
552 preserve_entry(r, query);
553 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"MySQL: entry preserved in %s",PRESERVEFILE);
554 } else {
555 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"MySQL: insert successful after a delayed retry.");
556 }
510 } 557 }
511 558
512 /* Restore SIGPIPE to its original handler function */ 559 /* Restore SIGPIPE to its original handler function */
513 signal(SIGPIPE, handler); 560 signal(SIGPIPE, handler);
514 561
515 if (retval) {
516 /* Attempt a single re-try... First sleep for a tiny amount of time. */
517 delay.tv_sec = 0;
518 delay.tv_nsec = 500000000; /* max is 999999999 (nine nines) */
519 ret = nanosleep(&delay, &remainder);
520 if (ret && errno != EINTR)
521 perror("nanosleep");
522
523 /* Now re-attempt */
524 retval = mysql_query(mysql_log,query);
525
526 if (retval) {
527 str = pstrcat(r->pool, "MySQL insert failed: ", query, NULL);
528 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,str);
529 str = pstrcat(r->pool, "MySQL failure reason: ", MYSQL_ERROR(mysql_log), NULL);
530 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,str);
531 } else {
532 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"MySQL: insert successful after a delayed retry.");
533 }
534 }
535 return retval; 562 return retval;
536} 563}
537 564
@@ -657,7 +684,7 @@ void *log_mysql_make_state(pool *p, server_rec *s)
657 684
658 685
659/* Setup of the available httpd.conf configuration commands. 686/* Setup of the available httpd.conf configuration commands.
660 * command, function called, NULL, where available, how many arguments, verbose description 687 * Structure: command, function called, NULL, where available, how many arguments, verbose description
661 */ 688 */
662command_rec log_mysql_cmds[] = { 689command_rec log_mysql_cmds[] = {
663 {"MySQLRefererLogTable", set_referer_log_mysql_table, NULL, RSRC_CONF, TAKE1, 690 {"MySQLRefererLogTable", set_referer_log_mysql_table, NULL, RSRC_CONF, TAKE1,
@@ -708,7 +735,8 @@ int log_mysql_transaction(request_rec *orig)
708 request_rec *r; 735 request_rec *r;
709 int retvalue = DECLINED; 736 int retvalue = DECLINED;
710 int referer_needed, agent_needed, transfer_needed; 737 int referer_needed, agent_needed, transfer_needed;
711 738
739
712 /* Are there configuration directives for these SQL logs? For each found 740 /* Are there configuration directives for these SQL logs? For each found
713 * config directive that is found, mark that type as 'needed'. 741 * config directive that is found, mark that type as 'needed'.
714 */ 742 */
@@ -719,13 +747,7 @@ int log_mysql_transaction(request_rec *orig)
719 if (!referer_needed && !agent_needed && !transfer_needed) { 747 if (!referer_needed && !agent_needed && !transfer_needed) {
720 return OK; 748 return OK;
721 } 749 }
722 750
723 if (mysql_log == NULL) { /* mysql link not up, hopefully we can do something about it */
724 open_logdb_link();
725 if (mysql_log == NULL) {
726 return OK;
727 }
728 }
729 751
730 for (r = orig; r->next; r = r->next) { 752 for (r = orig; r->next; r = r->next) {
731 continue; 753 continue;
@@ -750,7 +772,20 @@ int log_mysql_transaction(request_rec *orig)
750 } 772 }
751 } 773 }
752 str = pstrcat(orig->pool, "insert into ", cls->referer_table_name, " (referer,url,time_stamp) values ('", mysql_escape_log(referer, orig->pool), "','", mysql_escape_log(r->uri, orig->pool), "',unix_timestamp(now()) )", NULL); 774 str = pstrcat(orig->pool, "insert into ", cls->referer_table_name, " (referer,url,time_stamp) values ('", mysql_escape_log(referer, orig->pool), "','", mysql_escape_log(r->uri, orig->pool), "',unix_timestamp(now()) )", NULL);
753 safe_mysql_query(orig, str); 775
776 if (mysql_log == NULL) { /* child's mysql link not up, re-establish it */
777 open_logdb_link();
778 if (mysql_log == NULL) {
779 preserve_entry(r, str);
780 return OK;
781 } else {
782 ap_log_error(APLOG_MARK,NOTICELEVEL,orig->server,"MySQL: httpd child established database connection");
783 safe_mysql_query(orig, str);
784 }
785 } else {
786 safe_mysql_query(orig, str);
787 }
788
754 } 789 }
755 } 790 }
756 791
@@ -763,7 +798,19 @@ int log_mysql_transaction(request_rec *orig)
763 798
764 if (agent != NULL) { 799 if (agent != NULL) {
765 str = pstrcat(orig->pool, "insert into ", cls->agent_table_name, "(agent,time_stamp) values ('", mysql_escape_log(agent, orig->pool), "',unix_timestamp(now()) )", NULL); 800 str = pstrcat(orig->pool, "insert into ", cls->agent_table_name, "(agent,time_stamp) values ('", mysql_escape_log(agent, orig->pool), "',unix_timestamp(now()) )", NULL);
766 safe_mysql_query(orig, str); 801
802 if (mysql_log == NULL) { /* child's mysql link not up, re-establish it */
803 open_logdb_link();
804 if (mysql_log == NULL) {
805 preserve_entry(r, str);
806 return OK;
807 } else {
808 ap_log_error(APLOG_MARK,NOTICELEVEL,orig->server,"MySQL: httpd child established database connection");
809 safe_mysql_query(orig, str);
810 }
811 } else {
812 safe_mysql_query(orig, str);
813 }
767 } 814 }
768 } 815 }
769 816
@@ -771,7 +818,7 @@ int log_mysql_transaction(request_rec *orig)
771 if (transfer_needed) { 818 if (transfer_needed) {
772 const char *thehost; 819 const char *thehost;
773 820
774 char *fields = "", *values = "", *query; 821 char *fields = "", *values = "";
775 const char *formatted_item; 822 const char *formatted_item;
776 int i, j, length; 823 int i, j, length;
777 824
@@ -840,8 +887,29 @@ int log_mysql_transaction(request_rec *orig)
840 } 887 }
841 888
842 /* Set up the actual INSERT statement and execute it. */ 889 /* Set up the actual INSERT statement and execute it. */
843 query = pstrcat(orig->pool, "insert into ", cls->transfer_table_name, " (", fields, ") values (", values, ")", NULL); 890 str = pstrcat(orig->pool, "insert into ", cls->transfer_table_name, " (", fields, ") values (", values, ")", NULL);
844 safe_mysql_query(orig, query); 891
892
893 /* How's our mysql link integrity? */
894 if (mysql_log == NULL) {
895
896 /* Try to regain the link */
897 open_logdb_link();
898
899 if (mysql_log == NULL) {
900 /* Unable to re-establish a DB link, so assume that it's really
901 * gone and send the entry to the preserve file instead. */
902 preserve_entry(r, str);
903 return OK;
904 } else {
905 /* Whew, we got the DB link back */
906 ap_log_error(APLOG_MARK,NOTICELEVEL,orig->server,"MySQL: httpd child established database connection");
907 safe_mysql_query(orig, str);
908 }
909 } else {
910 /* Everything was fine */
911 safe_mysql_query(orig, str);
912 }
845 913
846 } 914 }
847 return retvalue; 915 return retvalue;
@@ -851,7 +919,20 @@ int log_mysql_transaction(request_rec *orig)
851/* Called on the exit of an httpd child process */ 919/* Called on the exit of an httpd child process */
852static void log_mysql_child_exit(server_rec *s, pool *p) 920static void log_mysql_child_exit(server_rec *s, pool *p)
853{ 921{
854 mysql_close(mysql_log); 922 mysql_close(mysql_log);
923}
924
925/* Called on the init of an httpd child process */
926static void log_mysql_child_init(server_rec *s, pool *p)
927{
928 int retval;
929
930 retval = open_logdb_link();
931 #ifdef DEBUG
932 if (retval > 0) {
933 ap_log_error(APLOG_MARK,DEBUGLEVEL,s,"MySQL: open_logdb_link successful");
934 }
935 #endif
855} 936}
856 937
857 938
@@ -874,7 +955,7 @@ module mysql_log_module = {
874 log_mysql_transaction, /* logger */ 955 log_mysql_transaction, /* logger */
875 NULL, /* header parser */ 956 NULL, /* header parser */
876#if MODULE_MAGIC_NUMBER >= 19970728 /* 1.3-dev or later support these additionals... */ 957#if MODULE_MAGIC_NUMBER >= 19970728 /* 1.3-dev or later support these additionals... */
877 NULL, /* child_init */ 958 log_mysql_child_init, /* child_init */
878 log_mysql_child_exit, /* process exit/cleanup */ 959 log_mysql_child_exit, /* process exit/cleanup */
879 NULL /* [#0] post read-request */ 960 NULL /* [#0] post read-request */
880#endif 961#endif