diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a046d9..5cd2fb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,6 +113,7 @@ set(SOURCES src/http_handler.cpp src/arena_allocator.cpp src/format.cpp + src/metric.cpp ${CMAKE_BINARY_DIR}/json_tokens.cpp) add_executable(weaseldb ${SOURCES}) @@ -152,10 +153,15 @@ target_compile_options(test_commit_request PRIVATE -UNDEBUG) add_executable( test_http_handler - tests/test_http_handler.cpp src/http_handler.cpp src/arena_allocator.cpp - src/connection.cpp src/connection_registry.cpp) + tests/test_http_handler.cpp + src/http_handler.cpp + src/arena_allocator.cpp + src/format.cpp + src/connection.cpp + src/connection_registry.cpp + src/metric.cpp) target_link_libraries(test_http_handler doctest::doctest llhttp_static - Threads::Threads perfetto) + Threads::Threads perfetto simdutf::simdutf) target_include_directories(test_http_handler PRIVATE src) target_compile_definitions(test_http_handler PRIVATE DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) @@ -170,6 +176,8 @@ add_executable( src/arena_allocator.cpp src/config.cpp src/http_handler.cpp + src/format.cpp + src/metric.cpp ${CMAKE_BINARY_DIR}/json_tokens.cpp) add_dependencies(test_server_connection_return generate_json_tokens) target_link_libraries( diff --git a/src/http_handler.cpp b/src/http_handler.cpp index 1426a62..8fddf48 100644 --- a/src/http_handler.cpp +++ b/src/http_handler.cpp @@ -1,10 +1,19 @@ #include "http_handler.hpp" -#include "arena_allocator.hpp" -#include "perfetto_categories.hpp" + #include #include #include +#include "arena_allocator.hpp" +#include "format.hpp" +#include "metric.hpp" +#include "perfetto_categories.hpp" + +auto requests_counter_family = metric::create_counter( + "weaseldb_http_requests_total", "Total http requests"); +thread_local auto metrics_counter = + requests_counter_family.create({{"path", "/metrics"}}); + // HttpConnectionState implementation HttpConnectionState::HttpConnectionState(ArenaAllocator &arena) : current_header_field_buf(ArenaStlAllocator(&arena)), @@ -227,10 +236,44 @@ void HttpHandler::handleDeleteRetention(Connection &conn, void HttpHandler::handleGetMetrics(Connection &conn, const HttpConnectionState &state) { - // TODO: Implement metrics collection and formatting - sendResponse(conn, 200, "text/plain", - "# WeaselDB metrics\nweaseldb_requests_total 0\n", - state.connection_close); + metrics_counter.inc(); + ArenaAllocator &arena = conn.get_arena(); + auto metrics_span = metric::render(arena); + + // Calculate total size for the response body + size_t total_size = 0; + for (const auto &sv : metrics_span) { + total_size += sv.size(); + } + + auto *http_state = static_cast(conn.user_data); + + // Build HTTP response headers using arena + std::string_view headers; + if (state.connection_close) { + headers = static_format( + arena, "HTTP/1.1 200 OK\r\n", + "Content-Type: text/plain; version=0.0.4\r\n", + "Content-Length: ", static_cast(total_size), "\r\n", + "X-Response-ID: ", static_cast(http_state->request_id), "\r\n", + "Connection: close\r\n", "\r\n"); + conn.close_after_send(); + } else { + headers = static_format( + arena, "HTTP/1.1 200 OK\r\n", + "Content-Type: text/plain; version=0.0.4\r\n", + "Content-Length: ", static_cast(total_size), "\r\n", + "X-Response-ID: ", static_cast(http_state->request_id), "\r\n", + "Connection: keep-alive\r\n", "\r\n"); + } + + // Send headers + conn.append_message(headers, false); + + // Send body in chunks + for (const auto &sv : metrics_span) { + conn.append_message(sv, false); + } } void HttpHandler::handleGetOk(Connection &conn,