Skip to content

Commit 648be34

Browse files
committed
feat: AccessLogConfig flush threshold parameter
1 parent cadc824 commit 648be34

8 files changed

Lines changed: 54 additions & 10 deletions

File tree

aeronet/objects/include/aeronet/access-log-config.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ struct AccessLogConfig {
2121
JSON, // JSON format (requires AERONET_ENABLE_GLAZE at build time)
2222
};
2323

24+
bool operator==(const AccessLogConfig&) const = default;
25+
2426
// Output sink. Default: None (disabled).
2527
Sink sink{Sink::None};
2628

@@ -31,6 +33,9 @@ struct AccessLogConfig {
3133
// Use this when the server is behind a trusted reverse proxy.
3234
bool useForwardedFor{false};
3335

36+
// Data will be flushed to sink once the internal buffer of the writer exceeds this size.
37+
uint32_t flushThresholdInBytes{8192U};
38+
3439
// File path for the access log when sink == File. Ignored otherwise.
3540
std::string filePath;
3641
};

aeronet/objects/test/reserved-headers_test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ namespace aeronet::http {
1010

1111
TEST(ReservedHeadersTest, ReservedResponseHeaderBasic) {
1212
EXPECT_TRUE(IsReservedResponseHeader(http::ContentLength));
13-
EXPECT_TRUE(IsReservedResponseHeader("date"));
13+
EXPECT_TRUE(IsReservedResponseHeader(http::Date));
1414
EXPECT_TRUE(IsReservedResponseHeader(http::Connection));
15-
EXPECT_TRUE(IsReservedResponseHeader("transfer-encoding"));
15+
EXPECT_TRUE(IsReservedResponseHeader(http::TransferEncoding));
1616
EXPECT_TRUE(IsReservedResponseHeader("te"));
1717
EXPECT_TRUE(IsReservedResponseHeader("trailer"));
1818
EXPECT_TRUE(IsReservedResponseHeader("upgrade"));

aeronet/server/include/aeronet/access-log-writer.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class AccessLogWriter {
3939

4040
RawChars32 _buffer;
4141
BaseFd _fileFd; // Valid if sink == File, ignored otherwise
42-
AccessLogConfig::Format _format = AccessLogConfig::Format::CLF;
42+
uint32_t _flushThresholdInBytes{};
43+
AccessLogConfig::Format _format{};
4344
AccessLogConfig::Sink _sink = AccessLogConfig::Sink::None;
4445
};
4546

aeronet/server/src/access-log-writer.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434

3535
namespace aeronet {
3636

37-
AccessLogWriter::AccessLogWriter(const AccessLogConfig& config) : _format(config.format), _sink(config.sink) {
37+
AccessLogWriter::AccessLogWriter(const AccessLogConfig& config)
38+
: _flushThresholdInBytes(config.flushThresholdInBytes), _format(config.format), _sink(config.sink) {
3839
if (_sink == AccessLogConfig::Sink::None) {
3940
return;
4041
}
@@ -69,9 +70,7 @@ void AccessLogWriter::log(const RequestMetrics& metrics) {
6970
formatJSON(metrics);
7071
}
7172

72-
static constexpr decltype(_buffer)::size_type kFlushThreshold = 8192;
73-
74-
if (_buffer.size() >= kFlushThreshold) {
73+
if (_buffer.size() >= _flushThresholdInBytes) {
7574
flush();
7675
}
7776
}

aeronet/server/src/http-response-dispatch.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ void SingleHttpServer::finalizeAndSendResponseForHttp1(ConnectionIt cnxIt, HttpR
164164
queueData(cnxIt, resp.finalizeForHttp1(_dateHeader.data(), request.version(), opts, &_config.globalHeaders,
165165
_config.minCapturedBodySize));
166166

167-
state.inBuffer.erase_front(consumedBytes);
168167
if (!keepAlive) {
169168
// Always request drain+close for non-keep-alive connections. The actual close
170169
// only triggers once outBuffer is empty (checked by canCloseConnectionForDrain),
@@ -175,6 +174,8 @@ void SingleHttpServer::finalizeAndSendResponseForHttp1(ConnectionIt cnxIt, HttpR
175174
emitRequestMetrics(request, respStatusCode, request.body().size(), state.requestsServed > 0);
176175
}
177176

177+
state.inBuffer.erase_front(consumedBytes);
178+
178179
// End the span after response is finalized
179180
request.end(respStatusCode);
180181
}

aeronet/server/src/single-http-server.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <utility>
1919

2020
#include "aeronet/accept-encoding-negotiation.hpp"
21+
#include "aeronet/access-log-config.hpp"
2122
#include "aeronet/connection-state.hpp"
2223
#include "aeronet/cors-policy.hpp"
2324
#include "aeronet/event-loop.hpp"
@@ -871,7 +872,8 @@ bool SingleHttpServer::dispatchAsyncHandler(ConnectionIt cnxIt, const AsyncReque
871872

872873
refreshKeepAliveDeadline(cnxIt);
873874
resumeAsyncHandler(cnxIt);
874-
return asyncState.active;
875+
876+
return true;
875877
}
876878

877879
void SingleHttpServer::resumeAsyncHandler(ConnectionIt cnxIt) {
@@ -1546,6 +1548,8 @@ void SingleHttpServer::applyPendingUpdates() {
15461548
const TLSConfig tlsBefore = _config.tls;
15471549
#endif
15481550

1551+
AccessLogConfig accessLogConfigBefore = _config.accessLog;
1552+
15491553
ApplyPendingUpdates(_updates.lock, _updates.config, _updates.hasConfig, _config, "config");
15501554

15511555
// Reinitialize components dependent on config values.
@@ -1567,6 +1571,9 @@ void SingleHttpServer::applyPendingUpdates() {
15671571
}
15681572
}
15691573
#endif
1574+
if (_config.accessLog != accessLogConfigBefore) {
1575+
_accessLog = AccessLogWriter(_config.accessLog);
1576+
}
15701577
}
15711578
if (_updates.hasRouter.load(std::memory_order_acquire)) {
15721579
ApplyPendingUpdates(_updates.lock, _updates.router, _updates.hasRouter, _router, "router");

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ All notable changes to aeronet are documented in this file.
4242
- **Slightly improved websocket upgrade handler**: minimized allocations and removed a copy.
4343
- **Improved rate limiter performance** by using a sharding on several locks instead of a unique one.
4444
- `Http2Config::maxPriorityTreeDepth` is now enforced - stream priority dependencies exceeding the configured depth (or forming a cycle) are clamped to the root instead of being applied.
45+
- `AccessLogConfig` now has a new parameter `flushThresholdInBytes`, with default value `8192` (was previously hardcoded to this value).
4546

4647
## Others
4748

tests/http-core_test.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <cstdint>
1616
#include <cstring>
1717
#include <filesystem>
18+
#include <fstream>
19+
#include <iterator>
1820
#include <memory>
1921
#include <ranges>
2022
#include <regex>
@@ -313,12 +315,40 @@ TEST(HttpBasic, SimpleGet) {
313315
return resp;
314316
});
315317
std::string resp = httpGet("/abc");
316-
ASSERT_FALSE(resp.empty());
317318
ASSERT_TRUE(resp.starts_with("HTTP/1.1 200"));
318319
ASSERT_TRUE(resp.contains("You requested: /abc"));
319320
ASSERT_TRUE(resp.contains("x-test=abc123"));
320321
}
321322

323+
TEST(HttpBasic, AccessLogWriterFile) {
324+
ts.router().setDefault([](const HttpRequestView& req) { return req.makeResponse("OK"); });
325+
326+
test::ScopedTempDir tmpDir;
327+
328+
std::string logFilePath = tmpDir.dirPath().string() + "/log.txt";
329+
330+
ts.postConfigUpdate([&logFilePath](HttpServerConfig& cfg) {
331+
cfg.accessLog.sink = AccessLogConfig::Sink::File;
332+
cfg.accessLog.filePath = logFilePath;
333+
cfg.accessLog.flushThresholdInBytes = 0;
334+
});
335+
336+
std::string resp = httpGet("/my-path");
337+
ASSERT_TRUE(resp.starts_with("HTTP/1.1 200"));
338+
339+
resp = httpGet("/other/path");
340+
ASSERT_TRUE(resp.starts_with("HTTP/1.1 200"));
341+
342+
// load contents of logFilePath
343+
std::ifstream ifs(logFilePath);
344+
std::string logFileContent((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
345+
346+
ts.postConfigUpdate([](HttpServerConfig& cfg) { cfg.accessLog.sink = AccessLogConfig::Sink::None; });
347+
348+
EXPECT_TRUE(logFileContent.contains(R"(GET /my-path HTTP/1.1" 200 0 )"));
349+
EXPECT_TRUE(logFileContent.contains(R"(GET /other/path HTTP/1.1" 200 0 )"));
350+
}
351+
322352
TEST(HttpKeepAlive, MultipleSequentialRequests) {
323353
ts.router().setDefault([](const HttpRequestView& req) {
324354
HttpResponse resp;

0 commit comments

Comments
 (0)