From 683171911e171188df449606e5e00950bcf53f95 Mon Sep 17 00:00:00 2001 From: Alexandre Flion Date: Sat, 4 Nov 2023 19:06:51 +0100 Subject: [PATCH] Refactor everything to use prometheus-cpp If only I had known about this earlier ;w; --- CMakeLists.txt | 21 ++++++ cubic-server/CMakeLists.txt | 8 ++- cubic-server/PrometheusExporter.cpp | 29 ++++++++ cubic-server/PrometheusExporter.hpp | 20 ++++++ cubic-server/Server.cpp | 42 ++---------- cubic-server/Server.hpp | 13 +++- cubic-server/http/CMakeLists.txt | 6 -- cubic-server/http/HttpConnection.cpp | 98 ---------------------------- cubic-server/http/HttpConnection.hpp | 57 ---------------- cubic-server/http/HttpServer.cpp | 40 ------------ cubic-server/http/HttpServer.hpp | 14 ---- cubic-server/main.cpp | 22 +------ 12 files changed, 96 insertions(+), 274 deletions(-) create mode 100644 cubic-server/PrometheusExporter.cpp create mode 100644 cubic-server/PrometheusExporter.hpp delete mode 100644 cubic-server/http/CMakeLists.txt delete mode 100644 cubic-server/http/HttpConnection.cpp delete mode 100644 cubic-server/http/HttpConnection.hpp delete mode 100644 cubic-server/http/HttpServer.cpp delete mode 100644 cubic-server/http/HttpServer.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index dbe1c705d..6e350d2d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,12 +11,14 @@ option(NO_GUI "Build without GUI" OFF) option(STATIC_LINK "Link the binary statically" OFF) option(GTEST "Build with Google Test" OFF) option(ARM "Build for ARM" OFF) +option(PROMETHEUS "Build with Prometheus support" ON) message(STATUS "Debugging network: ${DEBUG_NETWORK}") message(STATUS "Building without GUI: ${NO_GUI}") message(STATUS "Linking statically: ${STATIC_LINK}") message(STATUS "Building with Google Test: ${GTEST}") message(STATUS "Building for ARM: ${ARM}") +message(STATUS "Building with Prometheus support: ${PROMETHEUS}") if (USE_CLANG) find_program(CLANG_EXECUTABLE clang REQUIRED) @@ -152,6 +154,20 @@ target_include_directories(spdlog SYSTEM INTERFACE ${spdlog_include}) get_target_property(yaml_cpp_include yaml-cpp INTERFACE_INCLUDE_DIRECTORIES) target_include_directories(yaml-cpp SYSTEM INTERFACE ${yaml_cpp_include}) +if(PROMETHEUS) + FetchContent_Declare( + prometheus-cpp + GIT_REPOSITORY https://github.com/jupp0r/prometheus-cpp.git + GIT_TAG v1.1.0 + GIT_PROGRESS TRUE + GIT_SHALLOW TRUE + ) + + FetchContent_MakeAvailable( + prometheus-cpp + ) +endif() + if(GTEST) # if the flag GTEST is true FetchContent_Declare( googletest @@ -195,6 +211,7 @@ target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE PROGRAM_VERSION="${CMAKE_PROJECT_VERSION}" MINECRAFT_VERSION="${MINECRAFT_VERSION}" GUI_UNAVAILABLE=$ + PROMETHEUS_SUPPORT=$ ) if (STATIC_LINK) @@ -237,6 +254,10 @@ target_link_libraries (${CMAKE_PROJECT_NAME} PRIVATE $<$>:Boost::circular_buffer> ) +if (PROMETHEUS) +target_link_libraries (${CMAKE_PROJECT_NAME} PRIVATE prometheus-cpp::pull) +endif () + if (CMAKE_BUILD_TYPE MATCHES RELWITHDEBINFO) target_link_libraries (${CMAKE_PROJECT_NAME} PRIVATE asan diff --git a/cubic-server/CMakeLists.txt b/cubic-server/CMakeLists.txt index 3a76f02da..16a94984e 100644 --- a/cubic-server/CMakeLists.txt +++ b/cubic-server/CMakeLists.txt @@ -47,6 +47,13 @@ target_sources (${CMAKE_PROJECT_NAME} PRIVATE CompressionUtils.hpp ) +if(PROMETHEUS) +target_sources (${CMAKE_PROJECT_NAME} PRIVATE + PrometheusExporter.cpp + PrometheusExporter.hpp +) +endif() + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/plugins) if (GTEST) @@ -110,4 +117,3 @@ add_subdirectory (scoreboard) add_subdirectory (registry) add_subdirectory (entities) add_subdirectory (ai) -add_subdirectory (http) diff --git a/cubic-server/PrometheusExporter.cpp b/cubic-server/PrometheusExporter.cpp new file mode 100644 index 000000000..a80134666 --- /dev/null +++ b/cubic-server/PrometheusExporter.cpp @@ -0,0 +1,29 @@ +#include + +#include +#include + +#include "PrometheusExporter.hpp" +#include "options.hpp" + +PrometheusExporter::PrometheusExporter(const std::string &bind): + _exposer(bind) +{ +} + +void PrometheusExporter::registerMetrics() +{ + _registry = std::make_shared(); + + // Just an example of what you can put here + // To add anything else to the metrics follow the readme of prometheus-cpp: + // https://github.com/jupp0r/prometheus-cpp/blob/master/README.md + // clang-format off + UNUSED auto &packet_counter = prometheus::BuildCounter() + .Name("observed_packets_total") + .Help("Number of observed packets") + .Register(*_registry); + // clang-format on + + _exposer.RegisterCollectable(_registry); +} diff --git a/cubic-server/PrometheusExporter.hpp b/cubic-server/PrometheusExporter.hpp new file mode 100644 index 000000000..61a580dc7 --- /dev/null +++ b/cubic-server/PrometheusExporter.hpp @@ -0,0 +1,20 @@ +#ifndef D5AEBC4C_6028_4506_86F2_DA6261F54832 +#define D5AEBC4C_6028_4506_86F2_DA6261F54832 + +#include +#include + +#include "prometheus/exposer.h" +#include "prometheus/registry.h" + +class PrometheusExporter { +public: + PrometheusExporter(const std::string &bind); + void registerMetrics(); + +private: + prometheus::Exposer _exposer; + std::shared_ptr _registry; +}; + +#endif /* D5AEBC4C_6028_4506_86F2_DA6261F54832 */ diff --git a/cubic-server/Server.cpp b/cubic-server/Server.cpp index a1b43f660..2ecfca76e 100644 --- a/cubic-server/Server.cpp +++ b/cubic-server/Server.cpp @@ -14,6 +14,7 @@ #include +#include "PrometheusExporter.hpp" #include "Server.hpp" #include "World.hpp" @@ -26,7 +27,6 @@ #include "command_parser/commands/InventoryDump.hpp" #include "command_parser/commands/Teleport.hpp" #include "default/DefaultWorldGroup.hpp" -#include "http/HttpServer.hpp" #include "logging/logging.hpp" #include "registry/Biome.hpp" #include "registry/Chat.hpp" @@ -38,20 +38,10 @@ using boost::asio::ip::tcp; Server::Server(): _running(false), - // _sockfd(-1), _config(), _pluginManager(this), _toSend(1024) { - // _config.load("./config.yml"); - // _config.parse("./config.yml"); - // _config.parse(2, (const char * const *){"./CubicServer", "--nogui"}); - // _host = _config.getIP(); - // _port = _config.getPort(); - // _maxPlayer = _config.getMaxPlayers(); - // _motd = _config.getMotd(); - // _enforceWhitelist = _config.getEnforceWhitelist(); - _commands.emplace_back(std::make_unique()); _commands.emplace_back(std::make_unique()); _commands.emplace_back(std::make_unique()); @@ -166,8 +156,10 @@ void Server::launch(const configuration::ConfigHandler &config) _writeThread = std::thread(&Server::_writeLoop, this); - // http stuff - _httpThread = std::thread(http::launchHttpServer); + if (CONFIG["monitoring-prometheus-enable"].as()) { + _prometheusExporter = std::make_unique("0.0.0.0:4242"); + _prometheusExporter->registerMetrics(); + } _doAccept(); @@ -229,15 +221,6 @@ void Server::triggerClientCleanup(size_t clientID) _clients.erase(clientID); return; } - // boost::lockfree::queue toDelete(_clients.size()); - // for (auto [id, cli] : _clients) { - // if (!cli) { - // _clients.erase(id); - // } else if (cli->isDisconnected()) { - // cli->getThread().join(); - // _clients.erase(id); - // } - // } std::erase_if(_clients, [](const auto augh) { if (augh.second->isDisconnected()) { if (augh.second->getThread().joinable()) @@ -252,17 +235,6 @@ void Server::addCommand(std::unique_ptr command) { this->_commands. void Server::_doAccept() { - // while (_running) { - // boost::system::error_code ec; - // tcp::socket socket(_io_context); - - // _acceptor->accept(socket, ec); - // if (!ec) { - // std::shared_ptr _cli(new Client(std::move(socket), currentClientID)); - // _clients.emplace(currentClientID++, _cli); - // _cli->run(); - // } - // } tcp::socket *socket = new tcp::socket(_io_context); _acceptor->async_accept(*socket, [socket, this](const boost::system::error_code &error) { @@ -274,10 +246,6 @@ void Server::_doAccept() } delete socket; if (this->_running) { - // for (auto [id, cli] : _clients) { - // if (!cli || cli->isDisconnected()) // Somehow they can already be freed before we get here... - // _clients.erase(id); - // } this->_doAccept(); } }); diff --git a/cubic-server/Server.hpp b/cubic-server/Server.hpp index 0da285f94..320ae6cc5 100644 --- a/cubic-server/Server.hpp +++ b/cubic-server/Server.hpp @@ -43,6 +43,10 @@ #include "registry/MasterRegistry.hpp" +#if PROMETHEUS_SUPPORT == 1 +#include "PrometheusExporter.hpp" +#endif + constexpr char MC_VERSION[] = "1.19.3"; constexpr char MC_VERSION_BRANDING[] = "CubicServer 1.19.3"; constexpr uint16_t MC_PROTOCOL = 761; @@ -155,8 +159,13 @@ class Server { RSAEncryptionHandler _rsaKey; - // http stuff - std::thread _httpThread; +#if PROMETHEUS_SUPPORT == 1 + std::unique_ptr _prometheusExporter; + +public: + NODISCARD inline const PrometheusExporter &getPrometheusExporter() const { return *_prometheusExporter; } + NODISCARD inline PrometheusExporter &getPrometheusExporter() { return *_prometheusExporter; } +#endif public: NODISCARD inline bool isCompressed() const { return _config["compression"].as(); } diff --git a/cubic-server/http/CMakeLists.txt b/cubic-server/http/CMakeLists.txt deleted file mode 100644 index 965030a31..000000000 --- a/cubic-server/http/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -target_sources (${CMAKE_PROJECT_NAME} PRIVATE - HttpConnection.hpp - HttpConnection.cpp - HttpServer.hpp - HttpServer.cpp -) diff --git a/cubic-server/http/HttpConnection.cpp b/cubic-server/http/HttpConnection.cpp deleted file mode 100644 index 385dd9a40..000000000 --- a/cubic-server/http/HttpConnection.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "HttpConnection.hpp" -#include - -using tcp = boost::asio::ip::tcp; -namespace beast = boost::beast; // from -namespace bhttp = beast::http; // from -using tcp = boost::asio::ip::tcp; // from - -namespace http { - -HttpConnection::HttpConnection(tcp::socket socket): - _socket(std::move(socket)) -{ -} - -void HttpConnection::start() -{ - _readRequest(); - _checkDeadline(); -} - -void HttpConnection::_readRequest() -{ - auto self = shared_from_this(); - - bhttp::async_read(_socket, _buffer, _request, [self](beast::error_code ec, std::size_t bytes_transferred) { - boost::ignore_unused(bytes_transferred); - if (!ec) - self->_processRequest(); - }); -} - -void HttpConnection::_processRequest() -{ - _response.version(_request.version()); - _response.keep_alive(false); - - switch (_request.method()) { - case bhttp::verb::get: - _response.result(bhttp::status::ok); - _createResponse(); - break; - - default: - // We return responses indicating an error if - // we do not recognize the request method. - _response.result(bhttp::status::bad_request); - _response.set(bhttp::field::content_type, "text/plain"); - beast::ostream(_response.body()) << "Invalid request-method '" << std::string(_request.method_string()) << "'"; - break; - } - - _writeResponse(); -} - -void HttpConnection::_createResponse() -{ - if (_request.target() == "/metrics") { - _response.set(bhttp::field::content_type, "text/html"); - beast::ostream(_response.body()) << "\n" - << "Some title\n" - << "\n" - << "

Request count

\n" - << "

There have been " << 1 << " requests so far.

\n" - << "\n" - << "\n"; - } else { - _response.result(bhttp::status::not_found); - _response.set(bhttp::field::content_type, "text/plain"); - beast::ostream(_response.body()) << "File not found\r\n"; - } -} - -void HttpConnection::_writeResponse() -{ - auto self = shared_from_this(); - - _response.content_length(_response.body().size()); - - bhttp::async_write(_socket, _response, [self](beast::error_code ec, std::size_t) { - self->_socket.shutdown(tcp::socket::shutdown_send, ec); - self->_deadline.cancel(); - }); -} - -void HttpConnection::_checkDeadline() -{ - auto self = shared_from_this(); - - _deadline.async_wait([self](beast::error_code ec) { - if (!ec) { - // Close socket to cancel any outstanding operation. - self->_socket.close(ec); - } - }); -} - -} diff --git a/cubic-server/http/HttpConnection.hpp b/cubic-server/http/HttpConnection.hpp deleted file mode 100644 index 40ebbabe7..000000000 --- a/cubic-server/http/HttpConnection.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef B4EBB362_DE83_491A_ABED_DAE947C26FAB -#define B4EBB362_DE83_491A_ABED_DAE947C26FAB - -#include -#include -#include -#include -#include - -namespace http { - -constexpr auto maxTimePerConnection = 30; - -class HttpConnection : public std::enable_shared_from_this { - using tcp = boost::asio::ip::tcp; - -public: - HttpConnection(tcp::socket socket); - - // Initiate the asynchronous operations associated with the connection. - void start(); - -private: - // The socket for the currently connected client. - tcp::socket _socket; - - // The buffer for performing reads. - boost::beast::flat_buffer _buffer {8192}; - - // The request message. - boost::beast::http::request _request; - - // The response message. - boost::beast::http::response _response; - - // The timer for putting a deadline on connection processing. - boost::asio::steady_timer _deadline {_socket.get_executor(), std::chrono::seconds(maxTimePerConnection)}; - - // Asynchronously receive a complete request message. - void _readRequest(); - - // Determine what needs to be done with the request message. - void _processRequest(); - - // Construct a response message based on the program state. - void _createResponse(); - - // Asynchronously transmit the response message. - void _writeResponse(); - - // Check whether we have spent enough time on this connection. - void _checkDeadline(); -}; - -} - -#endif /* B4EBB362_DE83_491A_ABED_DAE947C26FAB */ diff --git a/cubic-server/http/HttpServer.cpp b/cubic-server/http/HttpServer.cpp deleted file mode 100644 index 5ff8a2f2b..000000000 --- a/cubic-server/http/HttpServer.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "HttpServer.hpp" -#include "HttpConnection.hpp" -#include "logging/logging.hpp" - -namespace beast = boost::beast; -namespace net = boost::asio; -using tcp = boost::asio::ip::tcp; - -namespace http { - -void relaunchHttpServer(tcp::acceptor &acceptor, tcp::socket &socket) -{ - acceptor.async_accept(socket, [&](beast::error_code ec) { - if (!ec) - std::make_shared(std::move(socket))->start(); - relaunchHttpServer(acceptor, socket); - }); -} - -void launchHttpServer() -{ - while (1) { - try { - const auto address = net::ip::make_address("0.0.0.0"); - const unsigned short port = 8080; - - net::io_context ioc {1}; - - tcp::acceptor acceptor {ioc, {address, port}}; - tcp::socket socket {ioc}; - relaunchHttpServer(acceptor, socket); - - ioc.run(); - } catch (const std::exception &e) { - LERROR("HttpServer error: {}", e.what()); - } - } -} - -} diff --git a/cubic-server/http/HttpServer.hpp b/cubic-server/http/HttpServer.hpp deleted file mode 100644 index a69b9fbad..000000000 --- a/cubic-server/http/HttpServer.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef AC8CBA06_158A_4D75_B638_395F9E43B6FF -#define AC8CBA06_158A_4D75_B638_395F9E43B6FF - -#include - -namespace http { - -void relaunchHttpServer(boost::asio::ip::tcp::acceptor &acceptor, boost::asio::ip::tcp::socket &socket); - -void launchHttpServer(); - -} - -#endif /* AC8CBA06_158A_4D75_B638_395F9E43B6FF */ diff --git a/cubic-server/main.cpp b/cubic-server/main.cpp index fd13788f2..8d557eb0d 100644 --- a/cubic-server/main.cpp +++ b/cubic-server/main.cpp @@ -174,25 +174,9 @@ auto initArgs(int argc, const char *const argv[]) .help("Enable prometheus data exporting") .valueFromConfig("monitoring", "prometheus", "enable") .valueFromEnvironmentVariable("CBSRV_MONITORING_PROMETHEUS_ENABLE") - .defaultValue(false); - - program.add("http-ip") - .help("The ip the http server will bind to") - .valueFromConfig("http", "ip") - .valueFromEnvironmentVariable("CBSRV_HTTP_IP") - .defaultValue("0.0.0.0"); - - program.add("http-port") - .help("The port the http server will listen on") - .valueFromConfig("http", "port") - .valueFromEnvironmentVariable("CBSRV_HTTP_PORT") - .defaultValue(8080); - - program.add("http-enable") - .help("Enable the built-in http server") - .valueFromConfig("http", "enable") - .valueFromEnvironmentVariable("CBSRV_HTTP_ENABLE") - .defaultValue(false); + .valueFromArgument("--monitoring-prometheus-enable") + .defaultValue(false) + .implicit(); // clang-format on try {