diff options
| author | 2002-04-02 20:19:30 +0000 | |
|---|---|---|
| committer | 2002-04-02 20:19:30 +0000 | |
| commit | c46c8ac083b851d35137ae455eef0c8622dc07c7 (patch) | |
| tree | 414e2953fc33173fa5c181f877eda116f2d845b7 | |
| 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.
| -rw-r--r-- | CHANGELOG | 17 | ||||
| -rw-r--r-- | INSTALL | 4 | ||||
| -rw-r--r-- | mod_log_sql.c | 195 |
3 files changed, 155 insertions, 61 deletions
| @@ -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 | ||
| 4 | TODO: | 4 | TODO: |
| 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 | ||
| 10 | CHANGES: | 11 | CHANGES: |
| 11 | 12 | ||
| 13 | 1.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 | |||
| 12 | 1.14: | 25 | 1.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. |
| @@ -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 | ||
| 4 | Requirements | 4 | Requirements |
| @@ -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 | ||
| 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 |
