nghttpx: Add --accesslog-write-early option

--accesslog-write-early option is analogous to HAProxy's logasap.  If
used, nghttpx writes access log when response header fields are
received from backend rather than when request transaction finishes.
This commit is contained in:
Tatsuhiro Tsujikawa 2017-01-11 19:27:08 +09:00
parent a2afd393ed
commit 685e926494
8 changed files with 56 additions and 3 deletions

View File

@ -157,6 +157,7 @@ OPTIONS = [
"client-psk-secrets", "client-psk-secrets",
"client-no-http2-cipher-black-list", "client-no-http2-cipher-black-list",
"client-ciphers", "client-ciphers",
"accesslog-write-early",
] ]
LOGVARS = [ LOGVARS = [

View File

@ -2307,6 +2307,10 @@ Logging:
Default: )" Default: )"
<< DEFAULT_ACCESSLOG_FORMAT << R"( << DEFAULT_ACCESSLOG_FORMAT << R"(
--accesslog-write-early
Write access log when response header fields are
received from backend rather than when request
transaction finishes.
--errorlog-file=<PATH> --errorlog-file=<PATH>
Set path to write error log. To reopen file, send USR1 Set path to write error log. To reopen file, send USR1
signal to nghttpx. stderr will be redirected to the signal to nghttpx. stderr will be redirected to the
@ -3126,6 +3130,7 @@ int main(int argc, char **argv) {
{SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST.c_str(), no_argument, {SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST.c_str(), no_argument,
&flag, 149}, &flag, 149},
{SHRPX_OPT_CLIENT_CIPHERS.c_str(), required_argument, &flag, 150}, {SHRPX_OPT_CLIENT_CIPHERS.c_str(), required_argument, &flag, 150},
{SHRPX_OPT_ACCESSLOG_WRITE_EARLY.c_str(), no_argument, &flag, 151},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
@ -3833,6 +3838,11 @@ int main(int argc, char **argv) {
// --client-ciphers // --client-ciphers
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CIPHERS, StringRef{optarg}); cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CIPHERS, StringRef{optarg});
break; break;
case 151:
// --accesslog-write-early
cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_WRITE_EARLY,
StringRef::from_lit("yes"));
break;
default: default:
break; break;
} }

View File

@ -1786,6 +1786,11 @@ int option_lookup_token(const char *name, size_t namelen) {
return SHRPX_OPTID_FRONTEND_READ_TIMEOUT; return SHRPX_OPTID_FRONTEND_READ_TIMEOUT;
} }
break; break;
case 'y':
if (util::strieq_l("accesslog-write-earl", name, 20)) {
return SHRPX_OPTID_ACCESSLOG_WRITE_EARLY;
}
break;
} }
break; break;
case 22: case 22:
@ -3290,6 +3295,10 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_CLIENT_CIPHERS: case SHRPX_OPTID_CLIENT_CIPHERS:
config->tls.client.ciphers = make_string_ref(config->balloc, optarg); config->tls.client.ciphers = make_string_ref(config->balloc, optarg);
return 0;
case SHRPX_OPTID_ACCESSLOG_WRITE_EARLY:
config->logging.access.write_early = util::strieq_l("yes", optarg);
return 0; return 0;
case SHRPX_OPTID_CONF: case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored"; LOG(WARN) << "conf: ignored";

View File

@ -325,6 +325,8 @@ constexpr auto SHRPX_OPT_CLIENT_PSK_SECRETS =
constexpr auto SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST = constexpr auto SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST =
StringRef::from_lit("client-no-http2-cipher-black-list"); StringRef::from_lit("client-no-http2-cipher-black-list");
constexpr auto SHRPX_OPT_CLIENT_CIPHERS = StringRef::from_lit("client-ciphers"); constexpr auto SHRPX_OPT_CLIENT_CIPHERS = StringRef::from_lit("client-ciphers");
constexpr auto SHRPX_OPT_ACCESSLOG_WRITE_EARLY =
StringRef::from_lit("accesslog-write-early");
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8; constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
@ -686,6 +688,9 @@ struct LoggingConfig {
StringRef file; StringRef file;
// Send accesslog to syslog, ignoring accesslog_file. // Send accesslog to syslog, ignoring accesslog_file.
bool syslog; bool syslog;
// Write accesslog when response headers are received from
// backend, rather than response body is received and sent.
bool write_early;
} access; } access;
struct { struct {
StringRef file; StringRef file;
@ -888,6 +893,7 @@ enum {
SHRPX_OPTID_ACCESSLOG_FILE, SHRPX_OPTID_ACCESSLOG_FILE,
SHRPX_OPTID_ACCESSLOG_FORMAT, SHRPX_OPTID_ACCESSLOG_FORMAT,
SHRPX_OPTID_ACCESSLOG_SYSLOG, SHRPX_OPTID_ACCESSLOG_SYSLOG,
SHRPX_OPTID_ACCESSLOG_WRITE_EARLY,
SHRPX_OPTID_ADD_FORWARDED, SHRPX_OPTID_ADD_FORWARDED,
SHRPX_OPTID_ADD_REQUEST_HEADER, SHRPX_OPTID_ADD_REQUEST_HEADER,
SHRPX_OPTID_ADD_RESPONSE_HEADER, SHRPX_OPTID_ADD_RESPONSE_HEADER,

View File

@ -138,7 +138,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
chunked_response_(false), chunked_response_(false),
expect_final_response_(false), expect_final_response_(false),
request_pending_(false), request_pending_(false),
request_header_sent_(false) { request_header_sent_(false),
accesslog_written_(false) {
auto &timeoutconf = get_config()->http2.timeout; auto &timeoutconf = get_config()->http2.timeout;
@ -906,7 +907,9 @@ void Downstream::disable_downstream_wtimer() {
disable_timer(loop, &downstream_wtimer_); disable_timer(loop, &downstream_wtimer_);
} }
bool Downstream::accesslog_ready() const { return resp_.http_status > 0; } bool Downstream::accesslog_ready() const {
return !accesslog_written_ && resp_.http_status > 0;
}
void Downstream::add_retry() { ++num_retry_; } void Downstream::add_retry() { ++num_retry_; }
@ -985,4 +988,6 @@ void Downstream::set_addr(const DownstreamAddr *addr) { addr_ = addr; }
const DownstreamAddr *Downstream::get_addr() const { return addr_; } const DownstreamAddr *Downstream::get_addr() const { return addr_; }
void Downstream::set_accesslog_written(bool f) { accesslog_written_ = f; }
} // namespace shrpx } // namespace shrpx

View File

@ -406,6 +406,8 @@ public:
const DownstreamAddr *get_addr() const; const DownstreamAddr *get_addr() const;
void set_accesslog_written(bool f);
enum { enum {
EVENT_ERROR = 0x1, EVENT_ERROR = 0x1,
EVENT_TIMEOUT = 0x2, EVENT_TIMEOUT = 0x2,
@ -489,6 +491,8 @@ private:
bool request_pending_; bool request_pending_;
// true if downstream request header is considered to be sent. // true if downstream request header is considered to be sent.
bool request_header_sent_; bool request_header_sent_;
// true if access.log has been written.
bool accesslog_written_;
}; };
} // namespace shrpx } // namespace shrpx

View File

@ -1090,11 +1090,15 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
int rv; int rv;
auto upstream = downstream->get_upstream(); auto upstream = downstream->get_upstream();
auto handler = upstream->get_client_handler();
const auto &req = downstream->request(); const auto &req = downstream->request();
auto &resp = downstream->response(); auto &resp = downstream->response();
auto &nva = resp.fs.headers(); auto &nva = resp.fs.headers();
auto config = get_config();
auto &loggingconf = config->logging;
downstream->set_expect_final_response(false); downstream->set_expect_final_response(false);
auto status = resp.fs.header(http2::HD__STATUS); auto status = resp.fs.header(http2::HD__STATUS);
@ -1147,7 +1151,7 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
// On upgrade sucess, both ends can send data // On upgrade sucess, both ends can send data
if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) { if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) {
// If resume_read fails, just drop connection. Not ideal. // If resume_read fails, just drop connection. Not ideal.
delete upstream->get_client_handler(); delete handler;
return -1; return -1;
} }
downstream->set_request_state(Downstream::HEADER_COMPLETE); downstream->set_request_state(Downstream::HEADER_COMPLETE);
@ -1184,6 +1188,11 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
resp.headers_only = true; resp.headers_only = true;
} }
if (loggingconf.access.write_early && downstream->accesslog_ready()) {
handler->write_accesslog(downstream);
downstream->set_accesslog_written(true);
}
rv = upstream->on_downstream_header_complete(downstream); rv = upstream->on_downstream_header_complete(downstream);
if (rv != 0) { if (rv != 0) {
// Handling early return (in other words, response was hijacked by // Handling early return (in other words, response was hijacked by

View File

@ -822,10 +822,14 @@ namespace {
int htp_hdrs_completecb(http_parser *htp) { int htp_hdrs_completecb(http_parser *htp) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
auto upstream = downstream->get_upstream(); auto upstream = downstream->get_upstream();
auto handler = upstream->get_client_handler();
const auto &req = downstream->request(); const auto &req = downstream->request();
auto &resp = downstream->response(); auto &resp = downstream->response();
int rv; int rv;
auto config = get_config();
auto &loggingconf = config->logging;
resp.http_status = htp->status_code; resp.http_status = htp->status_code;
resp.http_major = htp->http_major; resp.http_major = htp->http_major;
resp.http_minor = htp->http_minor; resp.http_minor = htp->http_minor;
@ -911,6 +915,11 @@ int htp_hdrs_completecb(http_parser *htp) {
downstream->set_chunked_response(false); downstream->set_chunked_response(false);
} }
if (loggingconf.access.write_early && downstream->accesslog_ready()) {
handler->write_accesslog(downstream);
downstream->set_accesslog_written(true);
}
if (upstream->on_downstream_header_complete(downstream) != 0) { if (upstream->on_downstream_header_complete(downstream) != 0) {
return -1; return -1;
} }