nghttpx: Rewrite logging system

This change rewrites logging system of nghttpx.  Previously access log
and error log are written to stderr or syslog and there was no option
to change stderr to something else.  With this change, file path of
access log and error log can be configured separately and logging to
regular file is now added.  To support rotating log, if SIGUSR1 signal
is received by nghttpx, it closes the current log files and reopen it
with the same name.  The format of access log is changed and has same
look of apache's.  But not all columns are not supported yet.
This commit is contained in:
Tatsuhiro Tsujikawa 2014-07-05 18:22:40 +09:00
parent 57230b4029
commit 0ce848a611
28 changed files with 561 additions and 275 deletions

View File

@ -113,7 +113,7 @@ NGHTTPX_SRCS = \
shrpx_ssl.cc shrpx_ssl.h \ shrpx_ssl.cc shrpx_ssl.h \
shrpx_thread_event_receiver.cc shrpx_thread_event_receiver.h \ shrpx_thread_event_receiver.cc shrpx_thread_event_receiver.h \
shrpx_worker.cc shrpx_worker.h \ shrpx_worker.cc shrpx_worker.h \
shrpx_accesslog.cc shrpx_accesslog.h shrpx_worker_config.cc shrpx_worker_config.h
if HAVE_SPDYLAY if HAVE_SPDYLAY
NGHTTPX_SRCS += shrpx_spdy_upstream.cc shrpx_spdy_upstream.h NGHTTPX_SRCS += shrpx_spdy_upstream.cc shrpx_spdy_upstream.h

View File

@ -35,6 +35,7 @@
#include <unistd.h> #include <unistd.h>
#include <getopt.h> #include <getopt.h>
#include <syslog.h> #include <syslog.h>
#include <signal.h>
#include <limits> #include <limits>
#include <cstdlib> #include <cstdlib>
@ -52,6 +53,7 @@
#include "shrpx_config.h" #include "shrpx_config.h"
#include "shrpx_listen_handler.h" #include "shrpx_listen_handler.h"
#include "shrpx_ssl.h" #include "shrpx_ssl.h"
#include "shrpx_worker_config.h"
#include "util.h" #include "util.h"
#include "app_helper.h" #include "app_helper.h"
#include "ssl.h" #include "ssl.h"
@ -60,6 +62,10 @@ using namespace nghttp2;
namespace shrpx { namespace shrpx {
namespace {
const int REOPEN_LOG_SIGNAL = SIGUSR1;
} // namespace
namespace { namespace {
void ssl_acceptcb(evconnlistener *listener, int fd, void ssl_acceptcb(evconnlistener *listener, int fd,
sockaddr *addr, int addrlen, void *arg) sockaddr *addr, int addrlen, void *arg)
@ -252,9 +258,28 @@ void save_pid()
} }
} // namespace } // namespace
namespace {
void reopen_log_signal_cb(evutil_socket_t sig, short events, void *arg)
{
auto listener_handler = static_cast<ListenHandler*>(arg);
if(LOG_ENABLED(INFO)) {
LOG(INFO) << "Reopening log files: worker_info(" << &worker_config << ")";
}
(void)reopen_log_files();
if(get_config()->num_worker > 1) {
listener_handler->worker_reopen_log_files();
}
}
} // namespace
namespace { namespace {
int event_loop() int event_loop()
{ {
int rv;
auto evbase = event_base_new(); auto evbase = event_base_new();
if(!evbase) { if(!evbase) {
LOG(FATAL) << "event_base_new() failed"; LOG(FATAL) << "event_base_new() failed";
@ -298,16 +323,47 @@ int event_loop()
// After that, we drop the root privileges if needed. // After that, we drop the root privileges if needed.
drop_privileges(); drop_privileges();
sigset_t signals;
sigemptyset(&signals);
sigaddset(&signals, REOPEN_LOG_SIGNAL);
rv = pthread_sigmask(SIG_BLOCK, &signals, nullptr);
if(rv != 0) {
LOG(ERROR) << "Blocking REOPEN_LOG_SIGNAL failed: " << strerror(rv);
}
if(get_config()->num_worker > 1) { if(get_config()->num_worker > 1) {
listener_handler->create_worker_thread(get_config()->num_worker); listener_handler->create_worker_thread(get_config()->num_worker);
} else if(get_config()->downstream_proto == PROTO_HTTP2) { } else if(get_config()->downstream_proto == PROTO_HTTP2) {
listener_handler->create_http2_session(); listener_handler->create_http2_session();
} }
rv = pthread_sigmask(SIG_UNBLOCK, &signals, nullptr);
if(rv != 0) {
LOG(ERROR) << "Unblocking REOPEN_LOG_SIGNAL failed: " << strerror(rv);
}
auto reopen_log_signal_event = evsignal_new(evbase, REOPEN_LOG_SIGNAL,
reopen_log_signal_cb,
listener_handler);
if(!reopen_log_signal_event) {
LOG(ERROR) << "evsignal_new failed";
} else {
rv = event_add(reopen_log_signal_event, nullptr);
if(rv < 0) {
LOG(ERROR) << "event_add for reopen_log_signal_event failed";
}
}
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
LOG(INFO) << "Entering event loop"; LOG(INFO) << "Entering event loop";
} }
event_base_loop(evbase, 0); event_base_loop(evbase, 0);
if(reopen_log_signal_event) {
event_free(reopen_log_signal_event);
}
if(evlistener4) { if(evlistener4) {
evconnlistener_free(evlistener4); evconnlistener_free(evlistener4);
} }
@ -399,11 +455,12 @@ void fill_default_config()
mod_config()->http2_max_concurrent_streams = 100; mod_config()->http2_max_concurrent_streams = 100;
mod_config()->add_x_forwarded_for = false; mod_config()->add_x_forwarded_for = false;
mod_config()->no_via = false; mod_config()->no_via = false;
mod_config()->accesslog = false; mod_config()->accesslog_file = nullptr;
mod_config()->accesslog_syslog = false;
mod_config()->errorlog_file = strcopy("/dev/stderr");
mod_config()->errorlog_syslog = false;
mod_config()->conf_path = strcopy("/etc/nghttpx/nghttpx.conf"); mod_config()->conf_path = strcopy("/etc/nghttpx/nghttpx.conf");
mod_config()->syslog = false;
mod_config()->syslog_facility = LOG_DAEMON; mod_config()->syslog_facility = LOG_DAEMON;
mod_config()->use_syslog = false;
// Default accept() backlog // Default accept() backlog
mod_config()->backlog = -1; mod_config()->backlog = -1;
mod_config()->ciphers = nullptr; mod_config()->ciphers = nullptr;
@ -419,7 +476,6 @@ void fill_default_config()
mod_config()->gid = 0; mod_config()->gid = 0;
mod_config()->backend_ipv4 = false; mod_config()->backend_ipv4 = false;
mod_config()->backend_ipv6 = false; mod_config()->backend_ipv6 = false;
mod_config()->tty = isatty(fileno(stderr));
mod_config()->cert_tree = nullptr; mod_config()->cert_tree = nullptr;
mod_config()->downstream_http_proxy_userinfo = nullptr; mod_config()->downstream_http_proxy_userinfo = nullptr;
mod_config()->downstream_http_proxy_host = nullptr; mod_config()->downstream_http_proxy_host = nullptr;
@ -719,8 +775,19 @@ Logging:
Set the severity level of log output. <LEVEL> Set the severity level of log output. <LEVEL>
must be one of INFO, WARNING, ERROR and FATAL. must be one of INFO, WARNING, ERROR and FATAL.
Default: WARNING Default: WARNING
--accesslog Print simple accesslog to stderr. --accesslog-file=<PATH>
--syslog Send log messages to syslog. Set path to write access log. To reopen file,
send USR1 signal to nghttpx.
--accesslog-syslog
Send access log to syslog. If this option is
used, --access-file option is ignored.
--errorlog-file=<PATH>
Set path to write error log. To reopen file,
send USR1 signal to nghttpx.
Default: )"
<< get_config()->errorlog_file.get() << R"(
--errorlog-syslog Send error log to syslog. If this option is
used, --errorlog-file option is ignored.
--syslog-facility=<FACILITY> --syslog-facility=<FACILITY>
Set syslog facility to <FACILITY>. Set syslog facility to <FACILITY>.
Default: )" Default: )"
@ -807,13 +874,12 @@ int main(int argc, char **argv)
{"frontend-write-timeout", required_argument, &flag, 4}, {"frontend-write-timeout", required_argument, &flag, 4},
{"backend-read-timeout", required_argument, &flag, 5}, {"backend-read-timeout", required_argument, &flag, 5},
{"backend-write-timeout", required_argument, &flag, 6}, {"backend-write-timeout", required_argument, &flag, 6},
{"accesslog", no_argument, &flag, 7}, {"accesslog-file", required_argument, &flag, 7},
{"backend-keep-alive-timeout", required_argument, &flag, 8}, {"backend-keep-alive-timeout", required_argument, &flag, 8},
{"frontend-http2-window-bits", required_argument, &flag, 9}, {"frontend-http2-window-bits", required_argument, &flag, 9},
{"pid-file", required_argument, &flag, 10}, {"pid-file", required_argument, &flag, 10},
{"user", required_argument, &flag, 11}, {"user", required_argument, &flag, 11},
{"conf", required_argument, &flag, 12}, {"conf", required_argument, &flag, 12},
{"syslog", no_argument, &flag, 13},
{"syslog-facility", required_argument, &flag, 14}, {"syslog-facility", required_argument, &flag, 14},
{"backlog", required_argument, &flag, 15}, {"backlog", required_argument, &flag, 15},
{"ciphers", required_argument, &flag, 16}, {"ciphers", required_argument, &flag, 16},
@ -850,6 +916,9 @@ int main(int argc, char **argv)
{"altsvc", required_argument, &flag, 54}, {"altsvc", required_argument, &flag, 54},
{"add-response-header", required_argument, &flag, 55}, {"add-response-header", required_argument, &flag, 55},
{"worker-frontend-connections", required_argument, &flag, 56}, {"worker-frontend-connections", required_argument, &flag, 56},
{"accesslog-syslog", no_argument, &flag, 57},
{"errorlog-file", required_argument, &flag, 58},
{"errorlog-syslog", no_argument, &flag, 59},
{nullptr, 0, nullptr, 0 } {nullptr, 0, nullptr, 0 }
}; };
@ -930,7 +999,7 @@ int main(int argc, char **argv)
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_WRITE_TIMEOUT, optarg); cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_WRITE_TIMEOUT, optarg);
break; break;
case 7: case 7:
cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG, "yes"); cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FILE, optarg);
break; break;
case 8: case 8:
// --backend-keep-alive-timeout // --backend-keep-alive-timeout
@ -950,10 +1019,6 @@ int main(int argc, char **argv)
// --conf // --conf
mod_config()->conf_path = strcopy(optarg); mod_config()->conf_path = strcopy(optarg);
break; break;
case 13:
// --syslog
cmdcfgs.emplace_back(SHRPX_OPT_SYSLOG, "yes");
break;
case 14: case 14:
// --syslog-facility // --syslog-facility
cmdcfgs.emplace_back(SHRPX_OPT_SYSLOG_FACILITY, optarg); cmdcfgs.emplace_back(SHRPX_OPT_SYSLOG_FACILITY, optarg);
@ -1102,6 +1167,18 @@ int main(int argc, char **argv)
// --worker-frontend-connections // --worker-frontend-connections
cmdcfgs.emplace_back(SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS, optarg); cmdcfgs.emplace_back(SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS, optarg);
break; break;
case 57:
// --accesslog-syslog
cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_SYSLOG, "yes");
break;
case 58:
// --errorlog-file
cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_FILE, optarg);
break;
case 59:
// --errorlog-syslog
cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_SYSLOG, "yes");
break;
default: default:
break; break;
} }
@ -1140,11 +1217,14 @@ int main(int argc, char **argv)
} }
} }
if(get_config()->syslog) { if(get_config()->accesslog_syslog || get_config()->errorlog_syslog) {
openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID, openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID,
get_config()->syslog_facility); get_config()->syslog_facility);
mod_config()->use_syslog = true; }
mod_config()->tty = false;
if(reopen_log_files() != 0) {
LOG(FATAL) << "Failed to open log file";
exit(EXIT_FAILURE);
} }
if(get_config()->npn_list.empty()) { if(get_config()->npn_list.empty()) {

View File

@ -1,146 +0,0 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "shrpx_accesslog.h"
#include <syslog.h>
#include <ctime>
#include <cstdio>
#include <cstring>
#include "shrpx_config.h"
#include "shrpx_downstream.h"
namespace shrpx {
namespace {
std::string get_datestr()
{
char buf[64];
time_t now = time(0);
if(ctime_r(&now, buf) == 0) {
buf[0] = '\0';
} else {
size_t len = strlen(buf);
if(len == 0) {
buf[0] = '\0';
} else {
buf[strlen(buf)-1] = '\0';
}
}
return buf;
}
} // namespace
void upstream_connect(const std::string& client_ip)
{
if(get_config()->use_syslog) {
syslog(LOG_INFO, "%s ACCEPT\n", client_ip.c_str());
return;
}
fprintf(stderr, "%s [%s] ACCEPT\n", client_ip.c_str(),
get_datestr().c_str());
fflush(stderr);
}
namespace {
const char* status_code_color(unsigned int status_code)
{
if(status_code <= 199) {
return "\033[1;36m";
} else if(status_code <= 299) {
return "\033[1;32m";
} else if(status_code <= 399) {
return "\033[1;34m";
} else if(status_code <= 499) {
return "\033[1;31m";
} else if(status_code <= 599) {
return "\033[1;35m";
} else {
return "";
}
}
} // namespace
void upstream_response(const std::string& client_ip, unsigned int status_code,
Downstream *downstream)
{
if(!downstream) {
if(get_config()->use_syslog) {
syslog(LOG_INFO, "%s %u 0 \"-\"\n", client_ip.c_str(), status_code);
return;
}
fprintf(stderr, "%s%s [%s] %u%s 0 \"-\"\n",
get_config()->tty ? status_code_color(status_code) : "",
client_ip.c_str(), get_datestr().c_str(),
status_code,
get_config()->tty ? "\033[0m" : "");
fflush(stderr);
return;
}
const char *path;
if(downstream->get_request_path().empty()) {
path = downstream->get_request_http2_authority().c_str();
} else {
path = downstream->get_request_path().c_str();
}
if(get_config()->use_syslog) {
syslog(LOG_INFO, "%s %u %d \"%s %s HTTP/%u.%u\"",
client_ip.c_str(),
status_code,
downstream->get_stream_id(),
downstream->get_request_method().c_str(),
path,
downstream->get_request_major(),
downstream->get_request_minor());
return;
}
fprintf(stderr, "%s [%s] %s%u%s stream(%d) \"%s %s HTTP/%u.%u\"\n",
client_ip.c_str(), get_datestr().c_str(),
get_config()->tty ? status_code_color(status_code) : "",
status_code,
get_config()->tty ? "\033[0m" : "",
downstream->get_stream_id(),
downstream->get_request_method().c_str(),
path,
downstream->get_request_major(),
downstream->get_request_minor());
fflush(stderr);
}
} // namespace shrpx

View File

@ -33,7 +33,6 @@
#include "shrpx_config.h" #include "shrpx_config.h"
#include "shrpx_http_downstream_connection.h" #include "shrpx_http_downstream_connection.h"
#include "shrpx_http2_downstream_connection.h" #include "shrpx_http2_downstream_connection.h"
#include "shrpx_accesslog.h"
#include "shrpx_ssl.h" #include "shrpx_ssl.h"
#include "shrpx_worker.h" #include "shrpx_worker.h"
#ifdef HAVE_SPDYLAY #ifdef HAVE_SPDYLAY

View File

@ -74,7 +74,10 @@ const char SHRPX_OPT_FRONTEND_READ_TIMEOUT[] = "frontend-read-timeout";
const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[] = "frontend-write-timeout"; const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[] = "frontend-write-timeout";
const char SHRPX_OPT_BACKEND_READ_TIMEOUT[] = "backend-read-timeout"; const char SHRPX_OPT_BACKEND_READ_TIMEOUT[] = "backend-read-timeout";
const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[] = "backend-write-timeout"; const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[] = "backend-write-timeout";
const char SHRPX_OPT_ACCESSLOG[] = "accesslog"; const char SHRPX_OPT_ACCESSLOG_FILE[] = "accesslog-file";
const char SHRPX_OPT_ACCESSLOG_SYSLOG[] = "accesslog-syslog";
const char SHRPX_OPT_ERRORLOG_FILE[] = "errorlog-file";
const char SHRPX_OPT_ERRORLOG_SYSLOG[] = "errorlog-syslog";
const char const char
SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[] = "backend-keep-alive-timeout"; SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[] = "backend-keep-alive-timeout";
const char const char
@ -89,7 +92,6 @@ const char SHRPX_OPT_BACKEND_NO_TLS[] = "backend-no-tls";
const char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[] = "backend-tls-sni-field"; const char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[] = "backend-tls-sni-field";
const char SHRPX_OPT_PID_FILE[] = "pid-file"; const char SHRPX_OPT_PID_FILE[] = "pid-file";
const char SHRPX_OPT_USER[] = "user"; const char SHRPX_OPT_USER[] = "user";
const char SHRPX_OPT_SYSLOG[] = "syslog";
const char SHRPX_OPT_SYSLOG_FACILITY[] = "syslog-facility"; const char SHRPX_OPT_SYSLOG_FACILITY[] = "syslog-facility";
const char SHRPX_OPT_BACKLOG[] = "backlog"; const char SHRPX_OPT_BACKLOG[] = "backlog";
const char SHRPX_OPT_CIPHERS[] = "ciphers"; const char SHRPX_OPT_CIPHERS[] = "ciphers";
@ -401,8 +403,26 @@ int parse_config(const char *opt, const char *optarg)
return 0; return 0;
} }
if(util::strieq(opt, SHRPX_OPT_ACCESSLOG)) { if(util::strieq(opt, SHRPX_OPT_ACCESSLOG_FILE)) {
mod_config()->accesslog = util::strieq(optarg, "yes"); mod_config()->accesslog_file = strcopy(optarg);
return 0;
}
if(util::strieq(opt, SHRPX_OPT_ACCESSLOG_SYSLOG)) {
mod_config()->accesslog_syslog = util::strieq(optarg, "yes");
return 0;
}
if(util::strieq(opt, SHRPX_OPT_ERRORLOG_FILE)) {
mod_config()->errorlog_file = strcopy(optarg);
return 0;
}
if(util::strieq(opt, SHRPX_OPT_ERRORLOG_SYSLOG)) {
mod_config()->errorlog_syslog = util::strieq(optarg, "yes");
return 0; return 0;
} }
@ -540,12 +560,6 @@ int parse_config(const char *opt, const char *optarg)
return 0; return 0;
} }
if(util::strieq(opt, SHRPX_OPT_SYSLOG)) {
mod_config()->syslog = util::strieq(optarg, "yes");
return 0;
}
if(util::strieq(opt, SHRPX_OPT_SYSLOG_FACILITY)) { if(util::strieq(opt, SHRPX_OPT_SYSLOG_FACILITY)) {
int facility = int_syslog_facility(optarg); int facility = int_syslog_facility(optarg);
if(facility == -1) { if(facility == -1) {

View File

@ -70,7 +70,10 @@ extern const char SHRPX_OPT_FRONTEND_READ_TIMEOUT[];
extern const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[]; extern const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[];
extern const char SHRPX_OPT_BACKEND_READ_TIMEOUT[]; extern const char SHRPX_OPT_BACKEND_READ_TIMEOUT[];
extern const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[]; extern const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[];
extern const char SHRPX_OPT_ACCESSLOG[]; extern const char SHRPX_OPT_ACCESSLOG_FILE[];
extern const char SHRPX_OPT_ACCESSLOG_SYSLOG[];
extern const char SHRPX_OPT_ERRORLOG_FILE[];
extern const char SHRPX_OPT_ERRORLOG_SYSLOG[];
extern const char SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[]; extern const char SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[];
extern const char SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS[]; extern const char SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS[];
extern const char SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS[]; extern const char SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS[];
@ -80,7 +83,6 @@ extern const char SHRPX_OPT_FRONTEND_NO_TLS[];
extern const char SHRPX_OPT_BACKEND_NO_TLS[]; extern const char SHRPX_OPT_BACKEND_NO_TLS[];
extern const char SHRPX_OPT_PID_FILE[]; extern const char SHRPX_OPT_PID_FILE[];
extern const char SHRPX_OPT_USER[]; extern const char SHRPX_OPT_USER[];
extern const char SHRPX_OPT_SYSLOG[];
extern const char SHRPX_OPT_SYSLOG_FACILITY[]; extern const char SHRPX_OPT_SYSLOG_FACILITY[];
extern const char SHRPX_OPT_BACKLOG[]; extern const char SHRPX_OPT_BACKLOG[];
extern const char SHRPX_OPT_CIPHERS[]; extern const char SHRPX_OPT_CIPHERS[];
@ -192,6 +194,8 @@ struct Config {
std::unique_ptr<char[]> verify_client_cacert; std::unique_ptr<char[]> verify_client_cacert;
std::unique_ptr<char[]> client_private_key_file; std::unique_ptr<char[]> client_private_key_file;
std::unique_ptr<char[]> client_cert_file; std::unique_ptr<char[]> client_cert_file;
std::unique_ptr<char[]> accesslog_file;
std::unique_ptr<char[]> errorlog_file;
FILE *http2_upstream_dump_request_header; FILE *http2_upstream_dump_request_header;
FILE *http2_upstream_dump_response_header; FILE *http2_upstream_dump_response_header;
nghttp2_option *http2_option; nghttp2_option *http2_option;
@ -235,20 +239,18 @@ struct Config {
bool client_proxy; bool client_proxy;
bool add_x_forwarded_for; bool add_x_forwarded_for;
bool no_via; bool no_via;
bool accesslog;
bool upstream_no_tls; bool upstream_no_tls;
bool downstream_no_tls; bool downstream_no_tls;
bool syslog; // Send accesslog to syslog, ignoring accesslog_file.
// This member finally decides syslog is used or not bool accesslog_syslog;
bool use_syslog; // Send errorlog to syslog, ignoring errorlog_file.
bool errorlog_syslog;
bool client; bool client;
// true if --client or --client-proxy are enabled. // true if --client or --client-proxy are enabled.
bool client_mode; bool client_mode;
bool insecure; bool insecure;
bool backend_ipv4; bool backend_ipv4;
bool backend_ipv6; bool backend_ipv6;
// true if stderr refers to a terminal and syslog is not used
bool tty;
bool http2_no_cookie_crumbling; bool http2_no_cookie_crumbling;
bool upstream_frame_debug; bool upstream_frame_debug;
}; };

View File

@ -40,6 +40,7 @@ namespace shrpx {
Downstream::Downstream(Upstream *upstream, int stream_id, int priority) Downstream::Downstream(Upstream *upstream, int stream_id, int priority)
: request_bodylen_(0), : request_bodylen_(0),
response_bodylen_(0),
upstream_(upstream), upstream_(upstream),
dconn_(nullptr), dconn_(nullptr),
response_body_buf_(nullptr), response_body_buf_(nullptr),
@ -408,6 +409,16 @@ void Downstream::set_request_connection_close(bool f)
request_connection_close_ = f; request_connection_close_ = f;
} }
void Downstream::set_request_user_agent(std::string user_agent)
{
request_user_agent_ = std::move(user_agent);
}
const std::string& Downstream::get_request_user_agent() const
{
return request_user_agent_;
}
bool Downstream::get_request_http2_expect_body() const bool Downstream::get_request_http2_expect_body() const
{ {
return request_http2_expect_body_; return request_http2_expect_body_;
@ -675,6 +686,16 @@ evbuffer* Downstream::get_response_body_buf()
return response_body_buf_; return response_body_buf_;
} }
void Downstream::add_response_bodylen(size_t amount)
{
response_bodylen_ += amount;
}
int64_t Downstream::get_response_bodylen() const
{
return response_bodylen_;
}
void Downstream::set_priority(int32_t pri) void Downstream::set_priority(int32_t pri)
{ {
priority_ = pri; priority_ = pri;

View File

@ -140,6 +140,8 @@ public:
void set_chunked_request(bool f); void set_chunked_request(bool f);
bool get_request_connection_close() const; bool get_request_connection_close() const;
void set_request_connection_close(bool f); void set_request_connection_close(bool f);
void set_request_user_agent(std::string user_agent);
const std::string& get_request_user_agent() const;
bool get_request_http2_expect_body() const; bool get_request_http2_expect_body() const;
void set_request_http2_expect_body(bool f); void set_request_http2_expect_body(bool f);
bool get_expect_100_continue() const; bool get_expect_100_continue() const;
@ -207,6 +209,8 @@ public:
int get_response_state() const; int get_response_state() const;
int init_response_body_buf(); int init_response_body_buf();
evbuffer* get_response_body_buf(); evbuffer* get_response_body_buf();
void add_response_bodylen(size_t amount);
int64_t get_response_bodylen() const;
nghttp2_error_code get_response_rst_stream_error_code() const; nghttp2_error_code get_response_rst_stream_error_code() const;
void set_response_rst_stream_error_code(nghttp2_error_code error_code); void set_response_rst_stream_error_code(nghttp2_error_code error_code);
// Inspects HTTP/1 response. This checks tranfer-encoding etc. // Inspects HTTP/1 response. This checks tranfer-encoding etc.
@ -230,6 +234,7 @@ private:
std::string request_method_; std::string request_method_;
std::string request_path_; std::string request_path_;
std::string request_user_agent_;
std::string request_http2_scheme_; std::string request_http2_scheme_;
std::string request_http2_authority_; std::string request_http2_authority_;
std::string assembled_request_cookie_; std::string assembled_request_cookie_;
@ -237,6 +242,8 @@ private:
// the length of request body // the length of request body
int64_t request_bodylen_; int64_t request_bodylen_;
// the length of response body
int64_t response_bodylen_;
Upstream *upstream_; Upstream *upstream_;
DownstreamConnection *dconn_; DownstreamConnection *dconn_;

View File

@ -26,6 +26,7 @@
#include "shrpx_config.h" #include "shrpx_config.h"
#include "shrpx_log.h" #include "shrpx_log.h"
#include "shrpx_worker_config.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"

View File

@ -39,6 +39,7 @@
#include "shrpx_error.h" #include "shrpx_error.h"
#include "shrpx_http.h" #include "shrpx_http.h"
#include "shrpx_http2_session.h" #include "shrpx_http2_session.h"
#include "shrpx_worker_config.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"

View File

@ -40,6 +40,7 @@
#include "shrpx_client_handler.h" #include "shrpx_client_handler.h"
#include "shrpx_ssl.h" #include "shrpx_ssl.h"
#include "shrpx_http.h" #include "shrpx_http.h"
#include "shrpx_worker_config.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"
#include "base64.h" #include "base64.h"
@ -1120,6 +1121,8 @@ int on_data_chunk_recv_callback(nghttp2_session *session,
return 0; return 0;
} }
downstream->add_response_bodylen(len);
auto upstream = downstream->get_upstream(); auto upstream = downstream->get_upstream();
rv = upstream->on_downstream_body(downstream, data, len, false); rv = upstream->on_downstream_body(downstream, data, len, false);
if(rv != 0) { if(rv != 0) {

View File

@ -35,7 +35,7 @@
#include "shrpx_downstream_connection.h" #include "shrpx_downstream_connection.h"
#include "shrpx_config.h" #include "shrpx_config.h"
#include "shrpx_http.h" #include "shrpx_http.h"
#include "shrpx_accesslog.h" #include "shrpx_worker_config.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"
#include "base64.h" #include "base64.h"
@ -310,6 +310,8 @@ int on_request_headers(Http2Upstream *upstream,
auto path = http2::get_unique_header(nva, ":path"); auto path = http2::get_unique_header(nva, ":path");
auto method = http2::get_unique_header(nva, ":method"); auto method = http2::get_unique_header(nva, ":method");
auto scheme = http2::get_unique_header(nva, ":scheme"); auto scheme = http2::get_unique_header(nva, ":scheme");
auto user_agent = http2::get_header(nva, "user-agent");
bool is_connect = method && "CONNECT" == method->value; bool is_connect = method && "CONNECT" == method->value;
bool having_host = http2::non_empty_value(host); bool having_host = http2::non_empty_value(host);
bool having_authority = http2::non_empty_value(authority); bool having_authority = http2::non_empty_value(authority);
@ -345,7 +347,7 @@ int on_request_headers(Http2Upstream *upstream,
downstream->set_request_http2_scheme(http2::value_to_str(scheme)); downstream->set_request_http2_scheme(http2::value_to_str(scheme));
downstream->set_request_http2_authority(http2::value_to_str(authority)); downstream->set_request_http2_authority(http2::value_to_str(authority));
downstream->set_request_path(http2::value_to_str(path)); downstream->set_request_path(http2::value_to_str(path));
downstream->set_request_user_agent(http2::value_to_str(user_agent));
if(!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { if(!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
downstream->set_request_http2_expect_body(true); downstream->set_request_http2_expect_body(true);
@ -1015,6 +1017,10 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
if(!downstream->get_upgraded()) { if(!downstream->get_upgraded()) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF; *data_flags |= NGHTTP2_DATA_FLAG_EOF;
upstream_accesslog(upstream->get_client_handler()->get_ipaddr(),
downstream->get_response_http_status(),
downstream);
if(nghttp2_session_get_stream_remote_close(session, stream_id) == 0) { if(nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
upstream->rst_stream(downstream, NGHTTP2_NO_ERROR); upstream->rst_stream(downstream, NGHTTP2_NO_ERROR);
} }
@ -1044,6 +1050,7 @@ int Http2Upstream::error_reply(Downstream *downstream,
{ {
int rv; int rv;
auto html = http::create_error_html(status_code); auto html = http::create_error_html(status_code);
downstream->set_response_http_status(status_code);
downstream->init_response_body_buf(); downstream->init_response_body_buf();
auto body = downstream->get_response_body_buf(); auto body = downstream->get_response_body_buf();
rv = evbuffer_add(body, html.c_str(), html.size()); rv = evbuffer_add(body, html.c_str(), html.size());
@ -1073,10 +1080,6 @@ int Http2Upstream::error_reply(Downstream *downstream,
<< nghttp2_strerror(rv); << nghttp2_strerror(rv);
DIE(); DIE();
} }
if(get_config()->accesslog) {
upstream_response(get_client_handler()->get_ipaddr(),
status_code, downstream);
}
return 0; return 0;
} }
@ -1188,10 +1191,10 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
ULOG(FATAL, this) << "nghttp2_submit_response() failed"; ULOG(FATAL, this) << "nghttp2_submit_response() failed";
return -1; return -1;
} }
if(get_config()->accesslog) {
upstream_response(get_client_handler()->get_ipaddr(), if(downstream->get_upgraded()) {
downstream->get_response_http_status(), upstream_accesslog(get_client_handler()->get_ipaddr(),
downstream); downstream->get_response_http_status(), downstream);
} }
downstream->clear_response_headers(); downstream->clear_response_headers();

View File

@ -30,6 +30,7 @@
#include "shrpx_config.h" #include "shrpx_config.h"
#include "shrpx_error.h" #include "shrpx_error.h"
#include "shrpx_http.h" #include "shrpx_http.h"
#include "shrpx_worker_config.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"
@ -234,7 +235,7 @@ int HttpDownstreamConnection::push_request_headers()
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
const char *hdrp; const char *hdrp;
std::string nhdrs; std::string nhdrs;
if(get_config()->tty) { if(worker_config.errorlog_tty) {
nhdrs = http::colorizeHeaders(hdrs.c_str()); nhdrs = http::colorizeHeaders(hdrs.c_str());
hdrp = nhdrs.c_str(); hdrp = nhdrs.c_str();
} else { } else {
@ -492,6 +493,9 @@ namespace {
int htp_bodycb(http_parser *htp, const char *data, size_t len) int htp_bodycb(http_parser *htp, const char *data, size_t len)
{ {
auto downstream = static_cast<Downstream*>(htp->data); auto downstream = static_cast<Downstream*>(htp->data);
downstream->add_response_bodylen(len);
return downstream->get_upstream()->on_downstream_body return downstream->get_upstream()->on_downstream_body
(downstream, reinterpret_cast<const uint8_t*>(data), len, true); (downstream, reinterpret_cast<const uint8_t*>(data), len, true);
} }

View File

@ -35,7 +35,7 @@
#include "shrpx_http.h" #include "shrpx_http.h"
#include "shrpx_config.h" #include "shrpx_config.h"
#include "shrpx_error.h" #include "shrpx_error.h"
#include "shrpx_accesslog.h" #include "shrpx_worker_config.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"
@ -150,8 +150,6 @@ int htp_hdrs_completecb(http_parser *htp)
downstream->set_request_connection_close(!http_should_keep_alive(htp)); downstream->set_request_connection_close(!http_should_keep_alive(htp));
downstream->inspect_http1_request();
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
std::stringstream ss; std::stringstream ss;
ss << downstream->get_request_method() << " " ss << downstream->get_request_method() << " "
@ -166,6 +164,15 @@ int htp_hdrs_completecb(http_parser *htp)
ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str(); ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str();
} }
downstream->normalize_request_headers();
auto& nva = downstream->get_request_headers();
auto user_agent = http2::get_header(nva, "user-agent");
downstream->set_request_user_agent(http2::value_to_str(user_agent));
downstream->inspect_http1_request();
if(get_config()->client_proxy && if(get_config()->client_proxy &&
downstream->get_request_method() != "CONNECT") { downstream->get_request_method() != "CONNECT") {
// Make sure that request path is an absolute URI. // Make sure that request path is an absolute URI.
@ -658,6 +665,12 @@ void https_downstream_eventcb(bufferevent *bev, short events, void *ptr)
int HttpsUpstream::error_reply(unsigned int status_code) int HttpsUpstream::error_reply(unsigned int status_code)
{ {
auto html = http::create_error_html(status_code); auto html = http::create_error_html(status_code);
auto downstream = get_downstream();
if(downstream) {
downstream->set_response_http_status(status_code);
}
std::string header; std::string header;
header.reserve(512); header.reserve(512);
header += "HTTP/1.1 "; header += "HTTP/1.1 ";
@ -677,14 +690,11 @@ int HttpsUpstream::error_reply(unsigned int status_code)
ULOG(FATAL, this) << "evbuffer_add() failed"; ULOG(FATAL, this) << "evbuffer_add() failed";
return -1; return -1;
} }
auto downstream = get_downstream();
if(downstream) { if(downstream) {
downstream->set_response_state(Downstream::MSG_COMPLETE); downstream->set_response_state(Downstream::MSG_COMPLETE);
} }
if(get_config()->accesslog) {
upstream_response(this->get_client_handler()->get_ipaddr(), status_code,
downstream);
}
return 0; return 0;
} }
@ -811,7 +821,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
const char *hdrp; const char *hdrp;
std::string nhdrs; std::string nhdrs;
if(get_config()->tty) { if(worker_config.errorlog_tty) {
nhdrs = http::colorizeHeaders(hdrs.c_str()); nhdrs = http::colorizeHeaders(hdrs.c_str());
hdrp = nhdrs.c_str(); hdrp = nhdrs.c_str();
} else { } else {
@ -824,9 +834,10 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
ULOG(FATAL, this) << "evbuffer_add() failed"; ULOG(FATAL, this) << "evbuffer_add() failed";
return -1; return -1;
} }
if(get_config()->accesslog) {
upstream_response(this->get_client_handler()->get_ipaddr(), if(downstream->get_upgraded()) {
downstream->get_response_http_status(), downstream); upstream_accesslog(this->get_client_handler()->get_ipaddr(),
downstream->get_response_http_status(), downstream);
} }
downstream->clear_response_headers(); downstream->clear_response_headers();
@ -879,6 +890,13 @@ int HttpsUpstream::on_downstream_body_complete(Downstream *downstream)
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
DLOG(INFO, downstream) << "HTTP response completed"; DLOG(INFO, downstream) << "HTTP response completed";
} }
if(!downstream->get_upgraded()) {
upstream_accesslog(get_client_handler()->get_ipaddr(),
downstream->get_response_http_status(),
downstream);
}
if(downstream->get_request_connection_close() || if(downstream->get_request_connection_close() ||
downstream->get_response_connection_close()) { downstream->get_response_connection_close()) {
auto handler = get_client_handler(); auto handler = get_client_handler();

View File

@ -52,7 +52,6 @@ ListenHandler::ListenHandler(event_base *evbase, SSL_CTX *sv_ssl_ctx,
rate_limit_group_(bufferevent_rate_limit_group_new rate_limit_group_(bufferevent_rate_limit_group_new
(evbase, get_config()->worker_rate_limit_cfg)), (evbase, get_config()->worker_rate_limit_cfg)),
worker_stat_(util::make_unique<WorkerStat>()), worker_stat_(util::make_unique<WorkerStat>()),
num_worker_(0),
worker_round_robin_cnt_(0) worker_round_robin_cnt_(0)
{} {}
@ -61,22 +60,33 @@ ListenHandler::~ListenHandler()
bufferevent_rate_limit_group_free(rate_limit_group_); bufferevent_rate_limit_group_free(rate_limit_group_);
} }
void ListenHandler::worker_reopen_log_files()
{
WorkerEvent wev;
memset(&wev, 0, sizeof(wev));
wev.type = REOPEN_LOG;
for(auto& info : workers_) {
bufferevent_write(info.bev, &wev, sizeof(wev));
}
}
void ListenHandler::create_worker_thread(size_t num) void ListenHandler::create_worker_thread(size_t num)
{ {
workers_.resize(num); workers_.resize(0);
num_worker_ = 0;
for(size_t i = 0; i < num; ++i) { for(size_t i = 0; i < num; ++i) {
int rv; int rv;
auto info = &workers_[num_worker_]; auto info = WorkerInfo();
rv = socketpair(AF_UNIX, SOCK_STREAM, 0, info->sv); rv = socketpair(AF_UNIX, SOCK_STREAM, 0, info.sv);
if(rv == -1) { if(rv == -1) {
LLOG(ERROR, this) << "socketpair() failed: errno=" << errno; LLOG(ERROR, this) << "socketpair() failed: errno=" << errno;
continue; continue;
} }
evutil_make_socket_nonblocking(info->sv[0]); evutil_make_socket_nonblocking(info.sv[0]);
evutil_make_socket_nonblocking(info->sv[1]); evutil_make_socket_nonblocking(info.sv[1]);
info->sv_ssl_ctx = sv_ssl_ctx_; info.sv_ssl_ctx = sv_ssl_ctx_;
info->cl_ssl_ctx = cl_ssl_ctx_; info.cl_ssl_ctx = cl_ssl_ctx_;
try { try {
auto thread = std::thread{start_threaded_worker, info}; auto thread = std::thread{start_threaded_worker, info};
thread.detach(); thread.detach();
@ -84,24 +94,26 @@ void ListenHandler::create_worker_thread(size_t num)
LLOG(ERROR, this) << "Could not start thread: code=" << error.code() LLOG(ERROR, this) << "Could not start thread: code=" << error.code()
<< " msg=" << error.what(); << " msg=" << error.what();
for(size_t j = 0; j < 2; ++j) { for(size_t j = 0; j < 2; ++j) {
close(info->sv[j]); close(info.sv[j]);
} }
continue; continue;
} }
auto bev = bufferevent_socket_new(evbase_, info->sv[0], auto bev = bufferevent_socket_new(evbase_, info.sv[0],
BEV_OPT_DEFER_CALLBACKS); BEV_OPT_DEFER_CALLBACKS);
if(!bev) { if(!bev) {
LLOG(ERROR, this) << "bufferevent_socket_new() failed"; LLOG(ERROR, this) << "bufferevent_socket_new() failed";
for(size_t j = 0; j < 2; ++j) { for(size_t j = 0; j < 2; ++j) {
close(info->sv[j]); close(info.sv[j]);
} }
continue; continue;
} }
info->bev = bev; info.bev = bev;
workers_.push_back(info);
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
LLOG(INFO, this) << "Created thread #" << num_worker_; LLOG(INFO, this) << "Created thread #" << workers_.size() - 1;
} }
++num_worker_;
} }
} }
@ -111,7 +123,7 @@ int ListenHandler::accept_connection(evutil_socket_t fd,
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
LLOG(INFO, this) << "Accepted connection. fd=" << fd; LLOG(INFO, this) << "Accepted connection. fd=" << fd;
} }
if(num_worker_ == 0) { if(get_config()->num_worker == 1) {
if(worker_stat_->num_connections >= if(worker_stat_->num_connections >=
get_config()->worker_frontend_connections) { get_config()->worker_frontend_connections) {
@ -138,10 +150,11 @@ int ListenHandler::accept_connection(evutil_socket_t fd,
client->set_http2_session(http2session_.get()); client->set_http2_session(http2session_.get());
return 0; return 0;
} }
size_t idx = worker_round_robin_cnt_ % num_worker_; size_t idx = worker_round_robin_cnt_ % workers_.size();
++worker_round_robin_cnt_; ++worker_round_robin_cnt_;
WorkerEvent wev; WorkerEvent wev;
memset(&wev, 0, sizeof(wev)); memset(&wev, 0, sizeof(wev));
wev.type = NEW_CONNECTION;
wev.client_fd = fd; wev.client_fd = fd;
memcpy(&wev.client_addr, addr, addrlen); memcpy(&wev.client_addr, addr, addrlen);
wev.client_addrlen = addrlen; wev.client_addrlen = addrlen;

View File

@ -56,6 +56,7 @@ public:
~ListenHandler(); ~ListenHandler();
int accept_connection(evutil_socket_t fd, sockaddr *addr, int addrlen); int accept_connection(evutil_socket_t fd, sockaddr *addr, int addrlen);
void create_worker_thread(size_t num); void create_worker_thread(size_t num);
void worker_reopen_log_files();
event_base* get_evbase() const; event_base* get_evbase() const;
int create_http2_session(); int create_http2_session();
private: private:
@ -70,7 +71,6 @@ private:
std::unique_ptr<Http2Session> http2session_; std::unique_ptr<Http2Session> http2session_;
bufferevent_rate_limit_group *rate_limit_group_; bufferevent_rate_limit_group *rate_limit_group_;
std::unique_ptr<WorkerStat> worker_stat_; std::unique_ptr<WorkerStat> worker_stat_;
size_t num_worker_;
unsigned int worker_round_robin_cnt_; unsigned int worker_round_robin_cnt_;
}; };

View File

@ -25,11 +25,20 @@
#include "shrpx_log.h" #include "shrpx_log.h"
#include <syslog.h> #include <syslog.h>
#include <unistd.h>
#include <inttypes.h>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <ctime>
#include <iostream>
#include "shrpx_config.h" #include "shrpx_config.h"
#include "shrpx_downstream.h"
#include "shrpx_worker_config.h"
#include "util.h"
using namespace nghttp2;
namespace shrpx { namespace shrpx {
@ -48,6 +57,28 @@ const char *SEVERITY_COLOR[] = {
}; };
} // namespace } // namespace
namespace {
std::string get_datestr()
{
return "";
// Format data like this:
// 03/Jul/2014:00:19:38 +0900
char buf[64];
struct tm tms;
auto now = time(nullptr);
if(localtime_r(&now, &tms) == nullptr) {
return "";
}
if(strftime(buf, sizeof(buf), "%d/%b/%Y:%T %z", &tms) == 0) {
return "";
}
return buf;
}
} // namespace
int Log::severity_thres_ = WARNING; int Log::severity_thres_ = WARNING;
void Log::set_severity_level(int severity) void Log::set_severity_level(int severity)
@ -90,26 +121,163 @@ Log::Log(int severity, const char *filename, int linenum)
Log::~Log() Log::~Log()
{ {
if(!log_enabled(severity_)) { int rv;
if(!log_enabled(severity_) ||
(worker_config.errorlog_fd == -1 && !get_config()->errorlog_syslog)) {
return; return;
} }
if(get_config()->use_syslog) { if(get_config()->errorlog_syslog) {
syslog(severity_to_syslog_level(severity_), "%s (%s:%d)", syslog(severity_to_syslog_level(severity_), "[%s] %s (%s:%d)",
stream_.str().c_str(), filename_, linenum_); SEVERITY_STR[severity_], stream_.str().c_str(),
filename_, linenum_);
return; return;
} }
fprintf(stderr, "[%s%s%s] %s\n %s(%s:%d)%s\n", char buf[4096];
get_config()->tty ? SEVERITY_COLOR[severity_] : "", auto tty = worker_config.errorlog_tty;
SEVERITY_STR[severity_],
get_config()->tty ? "\033[0m" : "", rv = snprintf(buf, sizeof(buf),
stream_.str().c_str(), "%s [%s%s%s] %s\n %s(%s:%d)%s\n",
get_config()->tty ? "\033[1;30m" : "", get_datestr().c_str(),
filename_, linenum_, tty ? SEVERITY_COLOR[severity_] : "",
get_config()->tty ? "\033[0m" : ""); SEVERITY_STR[severity_],
fflush(stderr); tty ? "\033[0m" : "",
stream_.str().c_str(),
tty ? "\033[1;30m" : "",
filename_, linenum_,
tty ? "\033[0m" : "");
if(rv < 0) {
return;
}
auto nwrite = std::min(static_cast<size_t>(rv), sizeof(buf) - 1);
write(worker_config.errorlog_fd, buf, nwrite);
}
void upstream_accesslog(const std::string& client_ip, unsigned int status_code,
Downstream *downstream)
{
if(worker_config.accesslog_fd == -1 && !get_config()->accesslog_syslog) {
return;
}
char buf[1024];
int rv;
const char *path;
const char *method;
unsigned int major, minor;
const char *user_agent;
int64_t response_bodylen;
if(!downstream) {
path = "-";
method = "-";
major = 1;
minor = 0;
user_agent = "-";
response_bodylen = 0;
} else {
if(downstream->get_request_path().empty()) {
path = downstream->get_request_http2_authority().c_str();
} else {
path = downstream->get_request_path().c_str();
}
method = downstream->get_request_method().c_str();
major = downstream->get_request_major();
minor = downstream->get_request_minor();
user_agent = downstream->get_request_user_agent().c_str();
response_bodylen = downstream->get_response_bodylen();
}
static const char fmt[] =
"%s - - [%s] \"%s %s HTTP/%u.%u\" %u %" PRId64 " \"-\" \"%s\"\n";
rv = snprintf(buf, sizeof(buf), fmt,
client_ip.c_str(),
get_datestr().c_str(),
method,
path,
major,
minor,
status_code,
response_bodylen,
user_agent);
if(rv < 0) {
return;
}
auto nwrite = std::min(static_cast<size_t>(rv), sizeof(buf) - 1);
if(get_config()->accesslog_syslog) {
syslog(LOG_INFO, "%s", buf);
return;
}
write(worker_config.accesslog_fd, buf, nwrite);
}
int reopen_log_files()
{
int res = 0;
if(worker_config.accesslog_fd != -1) {
close(worker_config.accesslog_fd);
worker_config.accesslog_fd = -1;
}
if(!get_config()->accesslog_syslog && get_config()->accesslog_file) {
worker_config.accesslog_fd =
util::reopen_log_file(get_config()->accesslog_file.get());
if(worker_config.accesslog_fd == -1) {
LOG(ERROR) << "Failed to open accesslog file "
<< get_config()->accesslog_file.get();
res = -1;
}
}
int new_errorlog_fd = -1;
if(!get_config()->errorlog_syslog && get_config()->errorlog_file) {
new_errorlog_fd = util::reopen_log_file(get_config()->errorlog_file.get());
if(new_errorlog_fd == -1) {
if(worker_config.errorlog_fd != -1) {
LOG(ERROR) << "Failed to open errorlog file "
<< get_config()->errorlog_file.get();
} else {
std::cerr << "Failed to open errorlog file "
<< get_config()->errorlog_file.get()
<< std::endl;
}
res = -1;
}
}
if(worker_config.errorlog_fd != -1) {
close(worker_config.errorlog_fd);
worker_config.errorlog_fd = -1;
worker_config.errorlog_tty = false;
}
if(new_errorlog_fd != -1) {
worker_config.errorlog_fd = new_errorlog_fd;
worker_config.errorlog_tty = isatty(worker_config.errorlog_fd);
}
return res;
} }
} // namespace shrpx } // namespace shrpx

View File

@ -31,6 +31,8 @@
namespace shrpx { namespace shrpx {
class Downstream;
#define ENABLE_LOG 1 #define ENABLE_LOG 1
#define LOG_ENABLED(SEVERITY) (ENABLE_LOG && Log::log_enabled(SEVERITY)) #define LOG_ENABLED(SEVERITY) (ENABLE_LOG && Log::log_enabled(SEVERITY))
@ -95,8 +97,13 @@ private:
static int severity_thres_; static int severity_thres_;
}; };
#define TTY_HTTP_HD (get_config()->tty ? "\033[1;34m" : "") #define TTY_HTTP_HD (worker_config.errorlog_tty ? "\033[1;34m" : "")
#define TTY_RST (get_config()->tty ? "\033[0m" : "") #define TTY_RST (worker_config.errorlog_tty ? "\033[0m" : "")
void upstream_accesslog(const std::string& client_ip, unsigned int status_code,
Downstream *downstream);
int reopen_log_files();
} // namespace shrpx } // namespace shrpx

View File

@ -36,7 +36,7 @@
#include "shrpx_downstream_connection.h" #include "shrpx_downstream_connection.h"
#include "shrpx_config.h" #include "shrpx_config.h"
#include "shrpx_http.h" #include "shrpx_http.h"
#include "shrpx_accesslog.h" #include "shrpx_worker_config.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"
@ -164,7 +164,9 @@ void on_ctrl_recv_callback
const char *scheme = nullptr; const char *scheme = nullptr;
const char *host = nullptr; const char *host = nullptr;
const char *method = nullptr; const char *method = nullptr;
const char *content_length = 0; const char *content_length = nullptr;
const char *user_agent = nullptr;
for(size_t i = 0; nv[i]; i += 2) { for(size_t i = 0; nv[i]; i += 2) {
if(strcmp(nv[i], ":path") == 0) { if(strcmp(nv[i], ":path") == 0) {
path = nv[i+1]; path = nv[i+1];
@ -177,6 +179,8 @@ void on_ctrl_recv_callback
} else if(nv[i][0] != ':') { } else if(nv[i][0] != ':') {
if(strcmp(nv[i], "content-length") == 0) { if(strcmp(nv[i], "content-length") == 0) {
content_length = nv[i+1]; content_length = nv[i+1];
} else if(strcmp(nv[i], "user-agent") == 0) {
user_agent = nv[i+1];
} }
downstream->add_request_header(nv[i], nv[i+1]); downstream->add_request_header(nv[i], nv[i+1]);
} }
@ -204,6 +208,10 @@ void on_ctrl_recv_callback
downstream->set_request_path(path); downstream->set_request_path(path);
} }
if(user_agent) {
downstream->set_request_user_agent(user_agent);
}
if(!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) { if(!(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN)) {
downstream->set_request_http2_expect_body(true); downstream->set_request_http2_expect_body(true);
} }
@ -773,6 +781,9 @@ ssize_t spdy_data_read_callback(spdylay_session *session,
downstream->get_response_state() == Downstream::MSG_COMPLETE) { downstream->get_response_state() == Downstream::MSG_COMPLETE) {
if(!downstream->get_upgraded()) { if(!downstream->get_upgraded()) {
*eof = 1; *eof = 1;
upstream_accesslog(upstream->get_client_handler()->get_ipaddr(),
downstream->get_response_http_status(), downstream);
} else { } else {
// For tunneling, issue RST_STREAM to finish the stream. // For tunneling, issue RST_STREAM to finish the stream.
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
@ -800,6 +811,7 @@ int SpdyUpstream::error_reply(Downstream *downstream, unsigned int status_code)
{ {
int rv; int rv;
auto html = http::create_error_html(status_code); auto html = http::create_error_html(status_code);
downstream->set_response_http_status(status_code);
downstream->init_response_body_buf(); downstream->init_response_body_buf();
auto body = downstream->get_response_body_buf(); auto body = downstream->get_response_body_buf();
rv = evbuffer_add(body, html.c_str(), html.size()); rv = evbuffer_add(body, html.c_str(), html.size());
@ -831,10 +843,7 @@ int SpdyUpstream::error_reply(Downstream *downstream, unsigned int status_code)
<< spdylay_strerror(rv); << spdylay_strerror(rv);
DIE(); DIE();
} }
if(get_config()->accesslog) {
upstream_response(get_client_handler()->get_ipaddr(),
status_code, downstream);
}
return 0; return 0;
} }
@ -949,10 +958,10 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream)
ULOG(FATAL, this) << "spdylay_submit_response() failed"; ULOG(FATAL, this) << "spdylay_submit_response() failed";
return -1; return -1;
} }
if(get_config()->accesslog) {
upstream_response(get_client_handler()->get_ipaddr(), if(downstream->get_upgraded()) {
downstream->get_response_http_status(), upstream_accesslog(get_client_handler()->get_ipaddr(),
downstream); downstream->get_response_http_status(), downstream);
} }
downstream->clear_response_headers(); downstream->clear_response_headers();

View File

@ -48,7 +48,6 @@
#include "shrpx_log.h" #include "shrpx_log.h"
#include "shrpx_client_handler.h" #include "shrpx_client_handler.h"
#include "shrpx_config.h" #include "shrpx_config.h"
#include "shrpx_accesslog.h"
#include "shrpx_worker.h" #include "shrpx_worker.h"
#include "util.h" #include "util.h"
#include "ssl.h" #include "ssl.h"
@ -476,10 +475,6 @@ ClientHandler* accept_connection
return nullptr; return nullptr;
} }
if(get_config()->accesslog) {
upstream_connect(host);
}
int val = 1; int val = 1;
rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<char *>(&val), sizeof(val)); reinterpret_cast<char *>(&val), sizeof(val));

View File

@ -31,6 +31,7 @@
#include "shrpx_client_handler.h" #include "shrpx_client_handler.h"
#include "shrpx_http2_session.h" #include "shrpx_http2_session.h"
#include "shrpx_worker.h" #include "shrpx_worker.h"
#include "shrpx_worker_config.h"
#include "util.h" #include "util.h"
using namespace nghttp2; using namespace nghttp2;
@ -68,6 +69,18 @@ void ThreadEventReceiver::on_read(bufferevent *bev)
<< sizeof(wev) << " Actual:" << nread; << sizeof(wev) << " Actual:" << nread;
continue; continue;
} }
if(wev.type == REOPEN_LOG) {
if(LOG_ENABLED(INFO)) {
LOG(INFO) << "Reopening log files: worker_info("
<< &worker_config << ")";
}
reopen_log_files();
continue;
}
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
TLOG(INFO, this) << "WorkerEvent: client_fd=" << wev.client_fd TLOG(INFO, this) << "WorkerEvent: client_fd=" << wev.client_fd
<< ", addrlen=" << wev.client_addrlen; << ", addrlen=" << wev.client_addrlen;

View File

@ -40,10 +40,20 @@ namespace shrpx {
class Http2Session; class Http2Session;
struct WorkerStat; struct WorkerStat;
enum WorkerEventType {
NEW_CONNECTION = 0x01,
REOPEN_LOG = 0x02
};
struct WorkerEvent { struct WorkerEvent {
sockaddr_union client_addr; WorkerEventType type;
size_t client_addrlen; union {
evutil_socket_t client_fd; struct {
sockaddr_union client_addr;
size_t client_addrlen;
evutil_socket_t client_fd;
};
};
}; };
class ThreadEventReceiver { class ThreadEventReceiver {

View File

@ -36,16 +36,17 @@
#include "shrpx_thread_event_receiver.h" #include "shrpx_thread_event_receiver.h"
#include "shrpx_log.h" #include "shrpx_log.h"
#include "shrpx_http2_session.h" #include "shrpx_http2_session.h"
#include "shrpx_worker_config.h"
#include "util.h" #include "util.h"
using namespace nghttp2; using namespace nghttp2;
namespace shrpx { namespace shrpx {
Worker::Worker(WorkerInfo *info) Worker::Worker(const WorkerInfo& info)
: sv_ssl_ctx_(info->sv_ssl_ctx), : sv_ssl_ctx_(info.sv_ssl_ctx),
cl_ssl_ctx_(info->cl_ssl_ctx), cl_ssl_ctx_(info.cl_ssl_ctx),
fd_(info->sv[1]) fd_(info.sv[1])
{} {}
Worker::~Worker() Worker::~Worker()
@ -76,6 +77,8 @@ void eventcb(bufferevent *bev, short events, void *arg)
void Worker::run() void Worker::run()
{ {
(void)reopen_log_files();
auto evbase = std::unique_ptr<event_base, decltype(&event_base_free)> auto evbase = std::unique_ptr<event_base, decltype(&event_base_free)>
(event_base_new(), event_base_free); (event_base_new(), event_base_free);
if(!evbase) { if(!evbase) {
@ -105,7 +108,7 @@ void Worker::run()
event_base_loop(evbase.get(), 0); event_base_loop(evbase.get(), 0);
} }
void start_threaded_worker(WorkerInfo *info) void start_threaded_worker(WorkerInfo info)
{ {
Worker worker(info); Worker worker(info);
worker.run(); worker.run();

View File

@ -42,7 +42,7 @@ struct WorkerStat {
class Worker { class Worker {
public: public:
Worker(WorkerInfo *info); Worker(const WorkerInfo& info);
~Worker(); ~Worker();
void run(); void run();
private: private:
@ -52,7 +52,7 @@ private:
int fd_; int fd_;
}; };
void start_threaded_worker(WorkerInfo *info); void start_threaded_worker(WorkerInfo info);
} // namespace shrpx } // namespace shrpx

View File

@ -1,7 +1,7 @@
/* /*
* nghttp2 - HTTP/2 C Library * nghttp2 - HTTP/2 C Library
* *
* Copyright (c) 2012 Tatsuhiro Tsujikawa * Copyright (c) 2014 Tatsuhiro Tsujikawa
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
@ -22,21 +22,17 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#ifndef SHRPX_ACCESSLOG_H #include "shrpx_worker_config.h"
#define SHRPX_ACCESSLOG_H
#include "shrpx.h"
#include <stdint.h>
namespace shrpx { namespace shrpx {
class Downstream; WorkerConfig::WorkerConfig()
: accesslog_fd(-1),
errorlog_fd(-1),
errorlog_tty(false)
{}
void upstream_connect(const std::string& client_ip); thread_local WorkerConfig worker_config;
void upstream_response(const std::string& client_ip, unsigned int status_code,
Downstream *downstream);
} // namespace shrpx } // namespace shrpx
#endif // SHRPX_LOG_H

46
src/shrpx_worker_config.h Normal file
View File

@ -0,0 +1,46 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SHRPX_WORKER_CONFIG_H
#define SHRPX_WORKER_CONFIG_H
#include "shrpx.h"
namespace shrpx {
struct WorkerConfig {
int accesslog_fd;
int errorlog_fd;
// true if errorlog_fd is referring to a terminal.
bool errorlog_tty;
WorkerConfig();
};
// We need WorkerConfig per thread
extern thread_local WorkerConfig worker_config;
} // namespace shrpx
#endif // SHRPX_WORKER_CONFIG_H

View File

@ -28,6 +28,8 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netdb.h> #include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cassert> #include <cassert>
#include <cstdio> #include <cstdio>
@ -585,6 +587,18 @@ bool numeric_host(const char *hostname)
return true; return true;
} }
int reopen_log_file(const char *path)
{
auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(fd == -1) {
return -1;
}
return fd;
}
} // namespace util } // namespace util
} // namespace nghttp2 } // namespace nghttp2

View File

@ -478,6 +478,11 @@ private:
bool numeric_host(const char *hostname); bool numeric_host(const char *hostname);
// Opens |path| with O_APPEND enabled. If file does not exist, it is
// created first. This function returns file descriptor referring the
// opened file if it succeeds, or -1.
int reopen_log_file(const char *path);
} // namespace util } // namespace util
} // namespace nghttp2 } // namespace nghttp2