Add prometheus metrics for Central controllers (#1969)

* add header-only prometheus lib to ext

* rename folder

* Undo rename directory

* prometheus simpleapi included on mac & linux

* wip

* wire up some controller stats

* Get windows building with prometheus

* bsd build flags for prometheus

* Fix multiple network join from environment entrypoint.sh.release (#1961)

* _bond_m guards _bond, not _paths_m (#1965)

* Fix: warning: mutex '_aqm_m' is not held on every path through here [-Wthread-safety-analysis] (#1964)

* Serve prom metrics from /metrics endpoint

* Add prom metrics for Central controller specific things

* reorganize metric initialization

* testing out a labled gauge on Networks

* increment error counter on throw

* Consolidate metrics definitions

Put all metric definitions into node/Metrics.hpp.  Accessed as needed
from there.

* Revert "testing out a labled gauge on Networks"

This reverts commit 499ed6d95e.

* still blows up but adding to the record for completeness right now

* Fix runtime issues with metrics

* Add metrics files to visual studio project

* Missed an "extern"

* add copyright headers to new files

* Add metrics for sent/received bytes (total)

* put /metrics endpoint behind auth

* sendto returns int on Win32

---------

Co-authored-by: Leonardo Amaral <leleobhz@users.noreply.github.com>
Co-authored-by: Brenton Bostick <bostick@gmail.com>
This commit is contained in:
Grant Limberg 2023-04-21 12:12:43 -07:00 committed by GitHub
commit 8e6e4ede6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
62 changed files with 4023 additions and 25 deletions

View file

@ -0,0 +1,36 @@
# Copyright 2021... by Maxim Gusev
#
# https://github.com/John-Jasper-Doe/http-client-lite
#
# Distributed under the MIT License.
# (See accompanying file LICENSE or copy at https://mit-license.org/)
cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
project(http-client-lite)
## Set output binary
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
## Set property
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_compile_options(-Wall -Werror -Wextra -Wpedantic -g -O0)
option(HTTP_CLIENT_LITE_OPT_BUILD_EXAMPLES "Build examples" OFF )
if(HTTP_CLIENT_LITE_OPT_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
# Interface library:
add_library(${PROJECT_NAME} INTERFACE)
target_sources(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/jdl/httpclientlite.h)
add_custom_target(${PROJECT_NAME}.hdr SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/include/jdl/httpclientlite.h)
target_include_directories(
${PROJECT_NAME}
INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")

View file

@ -0,0 +1,22 @@
The MIT License
Copyright (c) 2016 Christian C. Sachs
Copyright (c) 2021 Maxim Gusev
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.

View file

@ -0,0 +1,30 @@
# HTTP Client lite: C++ Cross-platform library only from single-file header-only
This is a lite, C++ cross-platform header-only client library for http request based
on [csachs/picohttpclient](https://github.com/csachs/picohttpclient) project.
A Lightweight HTTP 1.1 client where to quickly do very simple HTTP requests,
without adding larger dependencies to a project.
## License
http client lite is distributed under the [MIT License](https://github.com/john-jasper-doe/http-client-lite/blob/master/LICENSE).
## Example usage
To see how this can be used see the examples folders.
**Example:**
```C++
#include <jdl/httpclientlite.hpp>
...
using namespace jdl;
...
HTTPResponse response = HTTPClient::request(HTTPClient::GET, URI("http://example.com"));
cout << response.body << endl;
...
```

View file

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
project(http-client-lite-examples)
add_executable(${PROJECT_NAME}_simple_request simple_request.cpp)
target_link_libraries(${PROJECT_NAME}_simple_request PRIVATE http_client_lite)

View file

@ -0,0 +1,43 @@
/*
* example for httpclientlite.hxx
*/
#include <iostream>
#include <map>
#include <string>
#include <jdl/httpclientlite.h>
using namespace jdl;
int main(int argc, char *argv[]) {
if (argc == 1) {
std::cout << "Use " << argv[0] << " http://example.org" << std::endl;
return EXIT_SUCCESS;
}
HTTPResponse response = HTTPClient::request(HTTPClient::POST, URI(argv[1]));
if (!response.success) {
std::cout << "Request failed!" << std::endl;
return EXIT_FAILURE;
}
std::cout << "Request success" << endl;
std::cout << "Server protocol: " << response.protocol << std::endl;
std::cout << "Response code: " << response.response << std::endl;
std::cout << "Response string: " << response.responseString << std::endl;
std::cout << "Headers:" << std::endl;
for (stringMap::iterator it = response.header.begin(); it != response.header.end(); ++it) {
std::cout << "\t" << it->first << "=" << it->second << std::endl;
}
std::cout << response.body << std::endl;
return EXIT_SUCCESS;
}

View file

@ -0,0 +1,327 @@
/*
* httpclientlite.hpp
* ===========================================================================================
*
* The MIT License
*
* Copyright (c) 2016 Christian C. Sachs
* Copyright (c) 2021 Maxim G.
*
* 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.
*/
#pragma once
#if defined (__linux__)
# define PLATFORM_LINUX
#elif defined (_WIN32) || defined (_WIN64)
# define PLATFORM_WINDOWS
#else
/* TODO:
* - Added Apple OS */
/* warning: Unknown OS */
#endif
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <cstring>
#include <sstream>
#include <sys/types.h>
#if defined (PLATFORM_WINDOWS)
# include <WinSock2.h>
# include <WS2tcpip.h>
typedef SOCKET socktype_t;
typedef int socklen_t;
# pragma comment(lib, "ws2_32.lib")
#elif defined (PLATFORM_LINUX)
# include <unistd.h>
# include <sys/socket.h>
# include <netdb.h>
# define INVALID_SOCKET -1
# define closesocket(__sock) close(__sock)
typedef int socktype_t;
#endif /* PLATFORM_WINDOWS or PLATFORM_LINUX */
const std::string content_type = "Content-Type: text/plain; version=0.0.4; charset=utf-8";
namespace jdl {
void init_socket() {
#if defined (PLATFORM_WINDOWS)
WSADATA wsa_data;
WSAStartup(MAKEWORD(2, 2), &wsa_data);
#endif /* PLATFORM_WINDOWS */
}
void deinit_socket() {
#if defined (PLATFORM_WINDOWS)
WSACleanup();
#endif /* PLATFORM_WINDOWS */
}
class tokenizer {
public:
inline tokenizer(std::string &str) : str(str), position(0){}
inline std::string next(std::string search, bool returnTail = false) {
size_t hit = str.find(search, position);
if (hit == std::string::npos) {
if (returnTail) {
return tail();
} else {
return "";
}
}
size_t oldPosition = position;
position = hit + search.length();
return str.substr(oldPosition, hit - oldPosition);
}
inline std::string tail() {
size_t oldPosition = position;
position = str.length();
return str.substr(oldPosition);
}
private:
std::string str;
std::size_t position;
};
typedef std::map<std::string, std::string> stringMap;
struct URI {
inline void parseParameters() {
tokenizer qt(querystring);
do {
std::string key = qt.next("=");
if (key == "")
break;
parameters[key] = qt.next("&", true);
} while (true);
}
inline URI(std::string input, bool shouldParseParameters = false) {
tokenizer t = tokenizer(input);
protocol = t.next("://");
std::string hostPortString = t.next("/");
tokenizer hostPort(hostPortString);
host = hostPort.next(hostPortString[0] == '[' ? "]:" : ":", true);
if (host[0] == '[')
host = host.substr(1, host.size() - 1);
port = hostPort.tail();
if (port.empty())
port = "80";
address = t.next("?", true);
querystring = t.next("#", true);
hash = t.tail();
if (shouldParseParameters) {
parseParameters();
}
}
std::string protocol, host, port, address, querystring, hash;
stringMap parameters;
};
struct HTTPResponse {
bool success;
std::string protocol;
std::string response;
std::string responseString;
stringMap header;
std::string body;
inline HTTPResponse() : success(true){}
inline static HTTPResponse fail() {
HTTPResponse result;
result.success = false;
return result;
}
};
struct HTTPClient {
typedef enum {
m_options = 0,
m_get,
m_head,
m_post,
m_put,
m_delete,
m_trace,
m_connect
} HTTPMethod;
inline static const char *method2string(HTTPMethod method) {
const char *methods[] = {"OPTIONS", "GET", "HEAD", "POST", "PUT",
"DELETE", "TRACE", "CONNECT", nullptr};
return methods[method];
}
inline static socktype_t connectToURI(const URI& uri) {
struct addrinfo hints, *result, *rp;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
int getaddrinfo_result =
getaddrinfo(uri.host.c_str(), uri.port.c_str(), &hints, &result);
if (getaddrinfo_result != 0)
return -1;
socktype_t fd = INVALID_SOCKET;
for (rp = result; rp != nullptr; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd == INVALID_SOCKET) {
continue;
}
int connect_result = connect(fd, rp->ai_addr, static_cast<socklen_t>(rp->ai_addrlen));
if (connect_result == -1) {
// successfully created a socket, but connection failed. close it!
closesocket(fd);
fd = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo(result);
return fd;
}
inline static std::string bufferedRead(socktype_t fd) {
size_t initial_factor = 4, buffer_increment_size = 8192, buffer_size = 0,
bytes_read = 0;
std::string buffer;
buffer.resize(initial_factor * buffer_increment_size);
// do {
bytes_read = recv(fd, ((char*)buffer.c_str()) + buffer_size,
static_cast<socklen_t>(buffer.size() - buffer_size), 0);
buffer_size += bytes_read;
// if (bytes_read > 0 &&
// (buffer.size() - buffer_size) < buffer_increment_size) {
// buffer.resize(buffer.size() + buffer_increment_size);
// }
// } while (bytes_read > 0);
buffer.resize(buffer_size);
return buffer;
}
#define HTTP_NEWLINE "\r\n"
#define HTTP_SPACE " "
#define HTTP_HEADER_SEPARATOR ": "
inline static HTTPResponse request(HTTPMethod method, const URI& uri, const std::string& body = "") {
socktype_t fd = connectToURI(uri);
if (fd < 0)
return HTTPResponse::fail();
// string request = string(method2string(method)) + string(" /") +
// uri.address + ((uri.querystring == "") ? "" : "?") +
// uri.querystring + " HTTP/1.1" HTTP_NEWLINE "Host: " +
// uri.host + HTTP_NEWLINE
// "Accept: */*" HTTP_NEWLINE
// "Connection: close" HTTP_NEWLINE HTTP_NEWLINE;
std::string request = std::string(method2string(method)) + std::string(" /") +
uri.address + ((uri.querystring == "") ? "" : "?") + uri.querystring + " HTTP/1.1" + HTTP_NEWLINE +
"Host: " + uri.host + ":" + uri.port + HTTP_NEWLINE +
"Accept: */*" + HTTP_NEWLINE +
content_type + HTTP_NEWLINE +
"Content-Length: " + std::to_string(body.size()) + HTTP_NEWLINE + HTTP_NEWLINE +
body;
/*int bytes_written = */send(fd, request.c_str(), static_cast<socklen_t>(request.size()), 0);
std::string buffer = bufferedRead(fd);
closesocket(fd);
HTTPResponse result;
tokenizer bt(buffer);
result.protocol = bt.next(HTTP_SPACE);
result.response = bt.next(HTTP_SPACE);
result.responseString = bt.next(HTTP_NEWLINE);
std::string header = bt.next(HTTP_NEWLINE HTTP_NEWLINE);
result.body = bt.tail();
tokenizer ht(header);
do {
std::string key = ht.next(HTTP_HEADER_SEPARATOR);
if (key == "")
break;
result.header[key] = ht.next(HTTP_NEWLINE, true);
} while (true);
return result;
}
};
} /* jdl:: */