diff options
author | Christopher Powell | 2002-04-02 20:19:30 +0000 |
---|---|---|
committer | Christopher Powell | 2002-04-02 20:19:30 +0000 |
commit | c46c8ac083b851d35137ae455eef0c8622dc07c7 (patch) | |
tree | 414e2953fc33173fa5c181f877eda116f2d845b7 /mod_log_sql.c | |
parent | 8bf1ba3538a4de8c9ff0361ecae0178b9e95cdea (diff) |
This is a "pre" snapshot of 1.15. Numerous changes emphasizing how SQL
errors are reported and handled. Any missed queries during SQL downtime
are written out to a textfile.
Diffstat (limited to 'mod_log_sql.c')
-rw-r--r-- | mod_log_sql.c | 195 |
1 files changed, 138 insertions, 57 deletions
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 | ||
466 | void open_logdb_link() | 469 | int 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 | |||
488 | void 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 | ||
477 | int safe_mysql_query(request_rec *r, const char *query) | 500 | int 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 | */ |
662 | command_rec log_mysql_cmds[] = { | 689 | command_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 */ |
852 | static void log_mysql_child_exit(server_rec *s, pool *p) | 920 | static 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 */ | ||
926 | static 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 |